AT&T 2.0.19 Code drop, stage 2 73/38673/1
authorInstrumental <jcgmisc@stl.gathman.org>
Mon, 26 Mar 2018 20:49:56 +0000 (13:49 -0700)
committerInstrumental <jcgmisc@stl.gathman.org>
Mon, 26 Mar 2018 20:50:07 +0000 (13:50 -0700)
Issue-ID: AAF-197
Change-Id: Ifc93308f52c10d6ad82e99cd3ff5ddb900bf219a
Signed-off-by: Instrumental <jcgmisc@stl.gathman.org>
292 files changed:
cadi/.gitignore [new file with mode: 0644]
cadi/.project [new file with mode: 0644]
cadi/aaf/.gitignore [new file with mode: 0644]
cadi/aaf/.project [new file with mode: 0644]
cadi/aaf/jenkins-pom.xml [new file with mode: 0644]
cadi/aaf/pom.xml [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java [new file with mode: 0644]
cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/JU_ErrMessageTest.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/JU_CertsMarshalTest.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFLocator.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/JU_AAFAuthnTest.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_Lur2_0Call.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_PermEval.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_TestAccess.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/JU_PermEval.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_FastPerms.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthToken.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java [new file with mode: 0644]
cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java [new file with mode: 0644]
cadi/aaf/src/test/resources/cadi.properties [new file with mode: 0644]
cadi/aaf/src/test/resources/log4j.properties [new file with mode: 0644]
cadi/cass/.gitignore [new file with mode: 0644]
cadi/cass/.project [new file with mode: 0644]
cadi/cass/etc/cadi.properties [new file with mode: 0644]
cadi/cass/pom.xml [new file with mode: 0644]
cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticatedUser.java [new file with mode: 0644]
cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticator.java [new file with mode: 0644]
cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthorizer.java [new file with mode: 0644]
cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFBase.java [new file with mode: 0644]
cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticatedUserTest.java [new file with mode: 0644]
cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticator.java [new file with mode: 0644]
cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthorizerTest.java [new file with mode: 0644]
cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFBaseTest.java [new file with mode: 0644]
cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_CASS.java [new file with mode: 0644]
cadi/client/.gitignore [new file with mode: 0644]
cadi/client/pom.xml [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java [new file with mode: 0644]
cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_DNSLocator.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_GreatCircle.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_PropertyLocator.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/client/test/JU_DNSLocator.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/client/test/JU_HolderTest.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/client/test/JU_PropertyLocator.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java [new file with mode: 0644]
cadi/client/src/test/java/org/onap/aaf/client/test/JU_TestAccess.java [new file with mode: 0644]
cadi/core/.gitignore [new file with mode: 0644]
cadi/core/.project [new file with mode: 0644]
cadi/core/conf/.gitignore [new file with mode: 0644]
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/aaf/cadi/AES.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Access.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/BasicCred.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/BufferedServletInputStream.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CachingLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CadiException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CadiWrap.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Capacitor.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CmdLine.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/GetCred.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Locator.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/LocatorException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Lur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Permission.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/PropAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/SecuritySetter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/ServletContextAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Symm.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/Transmutate.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/TrustChecker.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/User.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/UserChain.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/GetAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfo.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZ.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/AccessGetter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiHTTPManip.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/FCGet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapPermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/NullPermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/RolesAllowed.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/filter/ServletImpl.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/lur/ConfigPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/lur/EpiLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/lur/NullLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/CachedBasicPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/Kind.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/OAuth2FormPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/StringTagLookup.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/TaggedPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/TrustPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/UnAuthPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/principal/X509Principal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpEpiTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/LoginPageTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/TafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/CertIdentity.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509HttpTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509Taf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/FQI.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/MaskFormatException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/MyConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/NetMask.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/SubStandardConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/TheConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/UserChainManip.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/util/Vars.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Action.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Match.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/wsse/WSSEParser.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XReader.java [new file with mode: 0644]
cadi/core/src/test/java/.gitignore [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Access.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Base64.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedCadiWrap.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedServletInputStream.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiExceptionTest.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiWrapTest.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Capacitor.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CmdLine.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Hash.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_PropAccess.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_ServletContextAccess.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Symm.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_TrustChecker.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_User.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/filter/JU_FCGetTest.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_ConfigPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_LocalLur.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_LocalPermission.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_NullLur.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_BasicPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_CachedBasicPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_Kind.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_OAuth2FormPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_StringTagLookup.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_TaggedPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_TrustPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_UnAuthPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_X509Principal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/taf/JU_NullTafRespTest.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_FQI.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_JsonOutputStream.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_MaskFormatException.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_NetMask.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_Split.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_TheConsole.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_UserChainManip.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_Vars.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/wsse/JU_WSSE_Read.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/test/wsse/JU_XReader.java [new file with mode: 0644]
cadi/core/test/AESKeyFile [new file with mode: 0644]
cadi/core/test/CBUSevent.xml [new file with mode: 0644]
cadi/core/test/cadi.properties [new file with mode: 0644]
cadi/core/test/cadi.properties.duplicate [new file with mode: 0644]
cadi/core/test/keyfile [new file with mode: 0644]
cadi/oauth-enduser/.gitignore [new file with mode: 0644]
cadi/oauth-enduser/.settings/.gitignore [new file with mode: 0644]
cadi/oauth-enduser/cadi.properties [new file with mode: 0644]
cadi/oauth-enduser/pom.xml [new file with mode: 0644]
cadi/oauth-enduser/src/.gitignore [new file with mode: 0644]
cadi/oauth-enduser/src/main/java/com/att/cadi/enduser/OAuthExample.java [new file with mode: 0644]
cadi/pom.xml [new file with mode: 0644]
cadi/shiro/.gitignore [new file with mode: 0644]
cadi/shiro/pom.xml [new file with mode: 0644]
cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java [new file with mode: 0644]
cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java [new file with mode: 0644]
cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java [new file with mode: 0644]
cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java [new file with mode: 0644]
cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java [new file with mode: 0644]
cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java [new file with mode: 0644]

diff --git a/cadi/.gitignore b/cadi/.gitignore
new file mode 100644 (file)
index 0000000..100951d
--- /dev/null
@@ -0,0 +1,2 @@
+/.settings/
+/.classpath
diff --git a/cadi/.project b/cadi/.project
new file mode 100644 (file)
index 0000000..544e2f3
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>cadi</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/cadi/aaf/.gitignore b/cadi/aaf/.gitignore
new file mode 100644 (file)
index 0000000..30c908e
--- /dev/null
@@ -0,0 +1,4 @@
+/.settings/
+/.classpath
+/logs/
+/target/
diff --git a/cadi/aaf/.project b/cadi/aaf/.project
new file mode 100644 (file)
index 0000000..8151d3d
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>cadi_aaf</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/cadi/aaf/jenkins-pom.xml b/cadi/aaf/jenkins-pom.xml
new file mode 100644 (file)
index 0000000..f1a0f0a
--- /dev/null
@@ -0,0 +1,257 @@
+<!--
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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====================================================
+ *
+-->
+<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>com.att.cadi</groupId>
+               <artifactId>parent</artifactId>
+               <version>1.2.2</version>
+               <relativePath>..</relativePath>
+       </parent>
+
+       <modelVersion>4.0.0</modelVersion>
+       <name>CADI AAF (Application Authorization Framework) LUR</name>
+       <packaging>jar</packaging>
+       <artifactId>cadi-aaf</artifactId>
+
+       <dependencies>
+               <dependency>
+                       <groupId>com.att.authz</groupId>
+                       <artifactId>authz-client</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>com.att.cadi</groupId>
+                       <artifactId>cadi-client</artifactId>
+               </dependency>
+               
+               <dependency>
+                       <groupId>com.att.aft</groupId>
+                       <artifactId>dme2</artifactId>
+                       <exclusions>
+                     <exclusion> 
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-log4j12</artifactId>
+                     </exclusion>
+                     <exclusion> 
+                       <groupId>log4j</groupId>
+                       <artifactId>log4j</artifactId>
+                     </exclusion>
+            </exclusions>
+               </dependency>
+
+               <!-- dependency>
+               <groupId>org.apache.cassandra</groupId>
+               <artifactId>cassandra-all</artifactId>
+               <version>2.1.2</version>
+               <scope>compile</scope>
+               <exclusions>
+                     <exclusion> 
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-log4j12</artifactId>
+                     </exclusion>
+                     <exclusion> 
+                       <groupId>log4j</groupId>
+                       <artifactId>log4j</artifactId>
+                     </exclusion>
+                   </exclusions> 
+               </dependency-->
+               
+       </dependencies>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>jaxb2-maven-plugin</artifactId>
+                               <version>1.3</version>
+                               <executions>
+                                       <execution>
+                                               <phase>generate-sources</phase>
+                                               <goals>
+                                                       <goal>xjc</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                               <configuration>
+                                       <schemaDirectory>src/main/xsd</schemaDirectory>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                               <configuration>
+                                       <archive>
+                                               <manifestEntries>
+                                                       <Sealed>true</Sealed>
+                                               </manifestEntries>
+                                       </archive>
+
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>test-jar</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>test-jar</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                               
+                       </plugin>
+
+                       <!-- We want to create a Jar with Rosetta built in (since I don't want 
+                               a separate deployment at this time Use this one as the jar to put in SWM 
+                               packages -->
+                       <plugin>
+                               <artifactId>maven-assembly-plugin</artifactId>
+                               <version>2.4</version>
+                               <configuration>
+                                       <classifier>tests</classifier>
+                                       <archive>
+                                               <manifestEntries>
+                                                       <Sealed>true</Sealed>
+                                               </manifestEntries>
+                                       </archive>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>full</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>single</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <descriptors>
+                                                               <descriptor>src/assemble/cadi-aaf.xml</descriptor>
+                                                       </descriptors>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+
+                       <plugin>
+                               <!-- Must put this in to turn on Signing, but Configuration itself is 
+                                       in Parent -->
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jarsigner-plugin</artifactId>
+                               <configuration>
+                                       <skip>true</skip>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>sign-full</id>
+                                               <goals>
+                                                       <goal>sign</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <archive>target/${project.artifactId}-${project.version}-full.jar</archive>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>verify-full</id>
+                                               <goals>
+                                                       <goal>verify</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <archive>target/${project.artifactId}-${project.version}-full.jar</archive>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>jaxb2-maven-plugin</artifactId>
+                               <version>1.3</version>
+                               <executions>
+                                       <execution>
+                                               <phase>generate-sources</phase>
+                                               <goals>
+                                                       <goal>xjc</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                               <configuration>
+                                       <schemaDirectory>src/main/xsd</schemaDirectory>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-deploy-plugin</artifactId>
+                               <version>2.6</version>
+                               <configuration>
+                                       <skip>false</skip>
+                               </configuration>
+                       </plugin>
+               </plugins>
+
+               <pluginManagement>
+                       <plugins>
+                               <!--This plugin's configuration is used to store Eclipse m2e settings 
+                                       only. It has no influence on the Maven build itself. -->
+                               <plugin>
+                                       <groupId>org.eclipse.m2e</groupId>
+                                       <artifactId>lifecycle-mapping</artifactId>
+                                       <version>1.0.0</version>
+                                       <configuration>
+                                               <lifecycleMappingMetadata>
+                                                       <pluginExecutions>
+                                                               <pluginExecution>
+                                                                       <pluginExecutionFilter>
+                                                                               <groupId>
+                                                                                       org.codehaus.mojo
+                                                                               </groupId>
+                                                                               <artifactId>
+                                                                                       jaxb2-maven-plugin
+                                                                               </artifactId>
+                                                                               <versionRange>
+                                                                                       [1.3,)
+                                                                               </versionRange>
+                                                                               <goals>
+                                                                                       <goal>xjc</goal>
+                                                                               </goals>
+                                                                       </pluginExecutionFilter>
+                                                                       <action>
+                                                                               <ignore></ignore>
+                                                                       </action>
+                                                               </pluginExecution>
+                                                       </pluginExecutions>
+                                               </lifecycleMappingMetadata>
+                                       </configuration>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+       </build>
+       
+       <distributionManagement>
+               <repository>
+                       <id>nexus</id>
+                       <name>attarch-releases</name>
+                       <url>http://mavencentral.it.att.com:8084/nexus/content/repositories/attarch-releases</url>
+               </repository>
+               <snapshotRepository>
+                       <id>nexus</id>
+                       <name>attarch-snapshots</name>
+                       <url>http://mavencentral.it.att.com:8084/nexus/content/repositories/attarch-snapshots</url>
+               </snapshotRepository>
+       </distributionManagement>
+
+</project>
diff --git a/cadi/aaf/pom.xml b/cadi/aaf/pom.xml
new file mode 100644 (file)
index 0000000..d7b6f55
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============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====================================================
+ *
+-->
+<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.aaf.cadi</groupId>
+               <artifactId>parent</artifactId>
+               <version>1.5.0-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>aaf-cadi-aaf</artifactId>
+       <name>AAF CADI AAF Connection Library</name>
+       <packaging>jar</packaging>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onap.aaf.auth</groupId>
+                       <artifactId>aaf-auth-client</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-client</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.onap.aaf.misc</groupId>
+                       <artifactId>aaf-misc-env</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-core</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+
+                       <!-- We want to create a Jar with Rosetta built in (since I don't want 
+                               a separate deployment at this time Use this one as the jar to put in SWM 
+                               packages <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> 
+                               <classifier>tests</classifier> <archive> <manifest> <mainClass>org.onap.aaf.cadi.cm.CmAgent</mainClass> 
+                               </manifest> <manifestEntries> <Sealed>true</Sealed> </manifestEntries> </archive> 
+                               </configuration> <executions> <execution> <id>full</id> <phase>package</phase> 
+                               <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>src/assemble/cadi-aaf.xml</descriptor> 
+                               </descriptors> </configuration> </execution> </executions> </plugin> -->
+               </plugins>
+       </build>
+</project>
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java
new file mode 100644 (file)
index 0000000..b3f0c4b
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Permission;
+
+/**
+ * A Class that understands the AAF format of Permission (name/type/action)
+ *  or String "name|type|action"
+ * 
+ * @author Jonathan
+ *
+ */
+public class AAFPermission implements Permission {
+       private static final List<String> NO_ROLES;
+       protected String type,instance,action,key;
+       private List<String> roles;
+       
+       static {
+               NO_ROLES = new ArrayList<String>();
+       }
+
+       protected AAFPermission() {roles=NO_ROLES;}
+
+       public AAFPermission(String type, String instance, String action) {
+               this.type = type;
+               this.instance = instance;
+               this.action = action;
+               key = type + '|' + instance + '|' + action;
+               this.roles = NO_ROLES;
+
+       }
+       public AAFPermission(String type, String instance, String action, List<String> roles) {
+               this.type = type;
+               this.instance = instance;
+               this.action = action;
+               key = type + '|' + instance + '|' + action;
+               this.roles = roles==null?NO_ROLES:roles;
+       }
+       
+       /**
+        * Match a Permission
+        * if Permission is Fielded type "Permission", we use the fields
+        * otherwise, we split the Permission with '|'
+        * 
+        * when the type or action starts with REGEX indicator character ( ! ),
+        * then it is evaluated as a regular expression.
+        * 
+        * If you want a simple field comparison, it is faster without REGEX
+        */
+       public boolean match(Permission p) {
+               if(p instanceof AAFPermission) {
+                       AAFPermission ap = (AAFPermission)p;
+                       // Note: In AAF > 1.0, Accepting "*" from name would violate multi-tenancy
+                       // Current solution is only allow direct match on Type.
+                       // 8/28/2014 Jonathan - added REGEX ability
+                       if(type.equals(ap.getName()))  
+                               if(PermEval.evalInstance(instance,ap.getInstance()))
+                                       if(PermEval.evalAction(action,ap.getAction()))
+                                               return true;
+               } else {
+                       // Permission is concatenated together: separated by |
+                       String[] aaf = p.getKey().split("[\\s]*\\|[\\s]*",3);
+                       if(aaf.length>0 && type.equals(aaf[0]))
+                               if(PermEval.evalInstance(instance,aaf.length>1?aaf[1]:"*"))
+                                       if(PermEval.evalAction(action,aaf.length>2?aaf[2]:"*"))
+                                               return true;
+               }                               
+               return false;
+       }
+
+       public String getName() {
+               return type;
+       }
+       
+       public String getInstance() {
+               return instance;
+       }
+       
+       public String getAction() {
+               return action;
+       }
+       
+       public String getKey() {
+               return key;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Permission#permType()
+        */
+       public String permType() {
+               return "AAF";
+       }
+
+       public List<String> roles() {
+               return roles;
+       }
+       public String toString() {
+               return "AAFPermission:\n\tType: " + type + 
+                               "\n\tInstance: " + instance +
+                               "\n\tAction: " + action +
+                               "\n\tKey: " + key;
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java
new file mode 100644 (file)
index 0000000..aa65504
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf;
+
+import org.onap.aaf.misc.env.util.Split;
+
+
+public class PermEval {
+       public static final char START_REGEX_CHAR = '!';
+       public static final char START_INST_KEY_CHAR=':';
+       public static final char ALT_START_INST_KEY_CHAR='/';
+       
+       public static final char LIST_SEP = ',';
+       public static final String INST_KEY_REGEX = new StringBuilder().append(START_INST_KEY_CHAR).toString();
+       public static final String ASTERIX = "*";
+       
+       /**
+        * Evaluate Instance
+        * 
+        * Instance can be more complex.  It can be a string, a Regular Expression, or a ":" separated Key 
+        * who's parts can also be a String, Regular Expression.
+        * 
+        * sInst = Server's Instance
+        * In order to prevent false matches, keys must be the same length to count as equal
+        * Changing this will break existing users, like Cassandra.  Jonathan 9-4-2015
+        */
+        public static boolean evalInstance(String sInst, String pInst) {
+                if(sInst==null || pInst == null) {
+                        return false;
+                }
+                 if(ASTERIX.equals(sInst)) {
+                         return true;                  // If Server's String is "*", then it accepts every Instance
+                 }
+                 char firstChar = pInst.charAt(0);
+                 char startChar = firstChar==ALT_START_INST_KEY_CHAR?ALT_START_INST_KEY_CHAR:START_INST_KEY_CHAR;
+                 switch(pInst.charAt(0)) {                                             // First char
+                       case START_REGEX_CHAR:                                                  // Evaluate as Regular Expression
+                               String pItem = pInst.substring(1);
+                           for(String sItem : Split.split(LIST_SEP,sInst)) {           // allow for "," definition in Action
+                               return sItem.matches(pItem);
+                           }
+                        
+                       case START_INST_KEY_CHAR:                                               // Evaluate a special Key field, i.e.:xyz:*:!df.*
+                       case ALT_START_INST_KEY_CHAR:                                   // Also allow '/' as special Key Field, i.e. /xyz/*/!.*
+                               if(sInst.charAt(0)==startChar) {  // To compare key-to-key, both strings must be keys
+                                       String[] skeys=Split.split(startChar,sInst);
+                                       String[] pkeys=Split.split(startChar,pInst);
+                                       if(skeys.length!=pkeys.length) return false;
+                                       
+                                       boolean pass = true;
+                                       for(int i=1;pass && i<skeys.length;++i) {                               // We start at 1, because the first one, being ":" is always ""
+                                               if(ASTERIX.equals(skeys[i]))continue;                           // Server data accepts all for this key spot
+                                               pass = false;
+                                           for(String sItem : Split.split(LIST_SEP,skeys[i])) {                // allow for "," definition in Action
+                                                       if(pkeys[i].length()==0) {
+                                                               if(pass=sItem.length()==0) {
+                                                                       break;                                                                  // Both Empty, keep checking
+                                                               }
+//                                                     } else if(pkeys[i].charAt(0)==START_REGEX_CHAR) { 
+//                                                             if(pass=sItem.matches(pkeys[i].substring(1))) {
+//                                                                     break;                                                                  // Matches, keep checking
+//                                                             }
+                                                       } else if(sItem.charAt(0)==START_REGEX_CHAR) { // Check Server side when wildcarding like *
+                                                               if(pass=pkeys[i].matches(sItem.substring(1))) {
+                                                                       break;                                                                  // Matches, keep checking
+                                                               }
+                                                       } else if(skeys[i].endsWith(ASTERIX)) {
+                                                               if(pass=endAsterixCompare(skeys[i],pkeys[i])) {
+                                                                       break;
+                                                               }
+                                                       } else {
+                                                               if(pass=sItem.equals(pkeys[i]))
+                                                                       break;                                                                  // Equal, keep checking
+                                                       }
+                                           }
+                                       }
+                                       return pass;                                                                                    // return whether passed all key checks
+                               }
+                               return false;                                                           // if first chars aren't the same, further String compare not necessary
+                       default:                                                                                // Evaluate as String Compare
+                           for(String sItem : Split.split(LIST_SEP,sInst)) {   // allow for "," separator //TODO is this only for actions?
+                               if(sItem.endsWith(ASTERIX)) {
+                                       if(endAsterixCompare(sInst, pInst));
+                               } else if(sItem.equals(pInst)) {
+                                       return true;
+                               }
+                           }
+                           return false;
+                 }
+        }
+        
+        private static boolean endAsterixCompare(String sInst, String pInst) {
+                       final int len = sInst.length()-1;
+                       if(pInst.length()<len) {
+                               return false;
+                       }
+                       for(int j=0;j<len;++j) {
+                               if(pInst.charAt(j)!=sInst.charAt(j)) {
+                                       return false;
+                               }
+                       }
+                       return true;
+       }
+
+       /**
+         * Evaluate Action
+         * 
+         * sAction = Stored Action...
+         * pAction = Present Action... the Permission to validate against.
+         * Action is not quite as complex.  But we write it in this function so it can be consistent
+         */
+         public static boolean evalAction(String sAction,String pAction) {
+                 if(ASTERIX.equals(sAction))return true;                    // If Server's String is "*", then it accepts every Action
+                 for(String sItem : Split.split(LIST_SEP,sAction)) {            // allow for "," definition in Action
+                         if (pAction.charAt(0)==START_REGEX_CHAR?       // First char
+                                     sItem.matches(pAction.substring(1)):   // Evaluate as Regular Expression
+                                     sItem.equals(pAction))                 // Evaluate as String Compare
+                                               return true;
+                 }             
+                 return false;
+         }
+        
+         /**
+          * Split.split by Char
+          * 
+          * Note: I read the String Split.split and Pattern Split.split code, and we can do this more efficiently for a single Character
+          */
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java
new file mode 100644 (file)
index 0000000..101fd60
--- /dev/null
@@ -0,0 +1,303 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HBasicAuthSS;
+import org.onap.aaf.cadi.http.HClient;
+import org.onap.aaf.cadi.http.HX509SS;
+import org.onap.aaf.cadi.oauth.HRenewingTokenSS;
+import org.onap.aaf.misc.env.APIException;
+
+public class TestConnectivity {
+       
+       public static void main(String[] args) {
+               if(args.length<1) {
+                       System.out.println("Usage: ConnectivityTester <cadi_prop_files> [<AAF FQDN (i.e. aaf.dev.att.com)>]");
+               } else {
+                       print(true,"START OF CONNECTIVITY TESTS",new Date().toString(),System.getProperty("user.name"),
+                                       "Note: All API Calls are /authz/perms/user/<MechID/Alias of the caller>");
+
+                       if(!args[0].contains(Config.CADI_PROP_FILES+'=')) {
+                               args[0]=Config.CADI_PROP_FILES+'='+args[0];
+                       }
+
+                       PropAccess access = new PropAccess(args);
+                       String aaflocate;
+                       if(args.length>1) {
+                               aaflocate = "https://" + args[1] + "/locate";
+                               access.setProperty(Config.AAF_LOCATE_URL, "https://" + args[1]);
+                       } else {
+                               aaflocate = access.getProperty(Config.AAF_LOCATE_URL);
+                               if(aaflocate==null) {
+                                       print(true,"Properties must contain ",Config.AAF_LOCATE_URL);
+                               }
+                       }
+                       
+                       try {
+                               SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class);
+                               
+                               List<SecuritySetter<HttpURLConnection>> lss = loadSetters(access,si);
+                               /////////
+                               print(true,"Test Connections driven by AAFLocator");
+                               URI serviceURI = new URI(aaflocate+"/AAF_NS.service/2.0");
+
+                               for(URI uri : new URI[] {
+                                               serviceURI,
+                                               new URI(aaflocate+"/AAF_NS.service:2.0"),
+                                               new URI(aaflocate+"/AAF_NS.service"),
+                                               new URI(aaflocate+"/AAF_NS.gw:2.0"),
+                                               new URI(aaflocate+"/AAF_NS.token:2.0"),
+                                               new URI(aaflocate+"/AAF_NS.certman:2.0"),
+                                               new URI(aaflocate+"/AAF_NS.hello")
+                               }) {
+                                       Locator<URI> locator = new AAFLocator(si, uri);
+                                       try {
+                                               connectTest(locator, uri);
+                                       } catch (Exception e) {
+                                               e.printStackTrace();
+                                               System.err.flush();
+                                       }
+                               }
+
+                               /////////
+                               print(true,"Test Service driven by AAFLocator");
+                               Locator<URI> locator = new AAFLocator(si,new URI(aaflocate+"/AAF_NS.service:2.0"));
+                               for(SecuritySetter<HttpURLConnection> ss : lss) {
+                                       permTest(locator,ss);
+                               }
+
+                               /////////
+                               print(true,"Test Proxy Access driven by AAFLocator");
+                               locator = new AAFLocator(si, new URI(aaflocate+"/AAF_NS.gw:2.0/proxy"));
+                               for(SecuritySetter<HttpURLConnection> ss : lss) {
+                                       permTest(locator,ss);
+                               }
+
+                               //////////
+                               print(true,"Test essential BasicAuth Service call, driven by AAFLocator");
+                               for(SecuritySetter<HttpURLConnection> ss : lss) {
+                                       if(ss instanceof HBasicAuthSS) {
+                                               basicAuthTest(new AAFLocator(si, new URI(aaflocate+"/AAF_NS.service:2.0")),ss);
+                                       }
+                               }
+                               
+                       } catch(Exception e) {
+                               e.printStackTrace(System.err);
+                       } finally {
+                               print(true,"END OF TESTS");
+                       }
+               }
+       }
+       
+       private static List<SecuritySetter<HttpURLConnection>> loadSetters(PropAccess access, SecurityInfoC<HttpURLConnection> si)  {
+               print(true,"Load Security Setters from Configuration Information");
+               String user = access.getProperty(Config.AAF_APPID);
+
+               ArrayList<SecuritySetter<HttpURLConnection>> lss = new ArrayList<SecuritySetter<HttpURLConnection>>();
+               
+
+               try {
+                       HBasicAuthSS hbass = new HBasicAuthSS(si,true);
+                       if(hbass==null || hbass.getID()==null) {
+                               access.log(Level.INFO, "BasicAuth Information is not available in configuration, BasicAuth tests will not be conducted... Continuing");
+                       } else {
+                               access.log(Level.INFO, "BasicAuth Information found with ID",hbass.getID(),".  BasicAuth tests will be performed.");
+                               lss.add(hbass);
+                       }
+               } catch (Exception e) {
+                       access.log(Level.INFO, "BasicAuth Security Setter constructor threw exception: \"",e.getMessage(),"\". BasicAuth tests will not be performed");
+               }
+
+               try {
+                       HX509SS hxss = new HX509SS(user,si);
+                       if(hxss==null || hxss.getID()==null) {
+                               access.log(Level.INFO, "X509 (Client certificate) Information is not available in configuration, X509 tests will not be conducted... Continuing");
+                       } else {
+                               access.log(Level.INFO, "X509 (Client certificate) Information found with ID",hxss.getID(),".  X509 tests will be performed.");
+                               lss.add(hxss);
+                       }
+               } catch (Exception e) {
+                       access.log(Level.INFO, "X509 (Client certificate) Security Setter constructor threw exception: \"",e.getMessage(),"\". X509 tests will not be performed");
+               }
+
+               String tokenURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL);
+               String locateURL=access.getProperty(Config.AAF_LOCATE_URL);
+               if(tokenURL==null || (tokenURL.contains("/locate/") && locateURL!=null)) {
+                       tokenURL=locateURL+"/locate/AAF_NS.token:2.0/token";
+               }
+
+               try {
+                       HRenewingTokenSS hrtss = new HRenewingTokenSS(access, tokenURL);
+                       access.log(Level.INFO, "AAF OAUTH2 Information found with ID",hrtss.getID(),".  AAF OAUTH2 tests will be performed.");
+                       lss.add(hrtss);
+               } catch (Exception e) {
+                       access.log(Level.INFO, "AAF OAUTH2 Security Setter constructor threw exception: \"",e.getMessage(),"\". AAF OAUTH2 tests will not be conducted... Continuing");
+               }
+               
+               tokenURL = access.getProperty(Config.AAF_ALT_OAUTH2_TOKEN_URL);
+               if(tokenURL==null) {
+                       access.log(Level.INFO, "AAF Alternative OAUTH2 requires",Config.AAF_ALT_OAUTH2_TOKEN_URL, "OAuth2 tests to", tokenURL, "will not be conducted... Continuing");
+               } else {
+                       try {
+                               HRenewingTokenSS hrtss = new HRenewingTokenSS(access, tokenURL);
+                               access.log(Level.INFO, "ALT OAUTH2 Information found with ID",hrtss.getID(),".  ALT OAUTH2 tests will be performed.");
+                               lss.add(hrtss);
+                       } catch (Exception e) {
+                               access.log(Level.INFO, "ALT OAUTH2 Security Setter constructor threw exception: \"",e.getMessage(),"\". ALT OAuth2 tests to", tokenURL, " will not be conducted... Continuing");
+                       }
+               }
+               
+               return lss;
+       }
+
+       private static void print(Boolean strong, String ... args) {
+               PrintStream out = System.out;
+               out.println();
+               if(strong) {
+                       for(int i=0;i<70;++i) {
+                               out.print('=');
+                       }
+                       out.println();
+               }
+               for(String s : args) {
+                       out.print(strong?"==  ":"------ ");
+                       out.print(s);
+                       if(!strong) {
+                               out.print("  ------");
+                       }
+                       out.println();
+               }
+               if(strong) {
+                       for(int i=0;i<70;++i) {
+                               out.print('=');
+                       }
+               }
+               out.println();
+       }
+
+       private static void connectTest(Locator<URI> dl, URI locatorURI) throws LocatorException {
+               URI uri;
+               Socket socket;
+               print(false,"TCP/IP Connect test to all Located Services for "  + locatorURI.toString() );
+               for(Item li = dl.first();li!=null;li=dl.next(li)) {
+                       if((uri = dl.get(li)) == null) {
+                               System.out.println("Locator Item empty");
+                       } else {
+                               try {
+                                       socket = new Socket();
+                                       socket.connect(new InetSocketAddress(uri.getHost(),  uri.getPort()),3000);
+                                       System.out.printf("Can Connect a Socket to %s %d\n",uri.getHost(),uri.getPort());
+                                       try {
+                                               socket.close();
+                                       } catch (IOException e1) {
+                                               System.out.printf("Could not close Socket Connection: %s\n",e1.getMessage());
+                                       }
+                               } catch (IOException e) {
+                                       System.out.printf("Cannot Connect a Socket to  %s %d: %s\n",uri.getHost(),uri.getPort(),e.getMessage());
+                               }
+                       }
+               }
+       }
+
+       private static void permTest(Locator<URI> dl, SecuritySetter<HttpURLConnection> ss)  {
+               try {
+                       URI uri = dl.get(dl.best());
+                       if(uri==null) {
+                               System.out.print("No URI available using " + ss.getClass().getSimpleName());
+                               System.out.println();
+                               return;
+                       } else {
+                               System.out.print("Resolved to: " + uri + " using " + ss.getClass().getSimpleName());
+                       }
+                       if(ss instanceof HRenewingTokenSS) {
+                               System.out.println(" " + ((HRenewingTokenSS)ss).tokenURL());
+                       } else {
+                               System.out.println();
+                       }
+                       HClient client = new HClient(ss, uri, 3000);
+                       client.setMethod("GET");
+                       String user = ss.getID();
+                       if(user.indexOf('@')<0) {
+                               user+="@isam.att.com";
+                       }
+                       client.setPathInfo("/authz/perms/user/"+user);
+                       client.send();
+                       Future<String> future = client.futureReadString();
+                       if(future.get(7000)) {
+                               System.out.println(future.body());      
+                       } else {
+                               if(future.code()==401 && ss instanceof HX509SS) {
+                                       System.out.println("  Authentication denied with 401 for Certificate.\n\t"
+                                                       + "This means Certificate isn't valid for this environment, and has attempted another method of Authentication");
+                               } else {
+                                       System.out.println(future.code() + ":" + future.body());
+                               }
+                       }
+               } catch (CadiException | LocatorException | APIException e) {
+                       e.printStackTrace();
+               }
+       }
+
+
+       private static void basicAuthTest(Locator<URI> dl, SecuritySetter<HttpURLConnection> ss) {
+               try {
+                       URI uri = dl.get(dl.best());
+                       System.out.println("Resolved to: " + uri);
+                       HClient client = new HClient(ss, uri, 3000);
+                       client.setMethod("GET");
+                       client.setPathInfo("/authn/basicAuth");
+                       client.addHeader("Accept", "text/plain");
+                       client.send();
+       
+               
+                       Future<String> future = client.futureReadString();
+                       if(future.get(7000)) {
+                               System.out.println("BasicAuth Validated");      
+                       } else {
+                               System.out.println("Failure " + future.code() + ":" + future.body());
+                       }
+               } catch (CadiException | LocatorException | APIException e) {
+                       e.printStackTrace();
+               }
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java
new file mode 100644 (file)
index 0000000..de8ae7d
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.cert;
+
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.principal.X509Principal;
+import org.onap.aaf.cadi.taf.cert.CertIdentity;
+import org.onap.aaf.cadi.taf.cert.X509Taf;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.misc.env.util.Split;
+
+import aaf.v2_0.Certs;
+import aaf.v2_0.Certs.Cert;
+import aaf.v2_0.Users;
+import aaf.v2_0.Users.User;
+
+public class AAFListedCertIdentity implements CertIdentity {
+       //TODO should 8 hours be configurable? 
+       private static final long EIGHT_HOURS = 1000*60*60*8; 
+                       
+       private static Map<ByteArrayHolder,String> certs = null;
+       
+       // Did this to add other Trust Mechanisms
+       // Trust mechanism set by Property: 
+       private static final String[] authMechanisms = new String[] {"tguard","basicAuth","csp"};
+       private static String[] certIDs;
+       
+       private static Map<String,Set<String>> trusted =null;
+
+       public AAFListedCertIdentity(Access access, AAFCon<?> aafcon) throws APIException {
+               synchronized(AAFListedCertIdentity.class) {
+                       if(certIDs==null) {
+                               String cip = access.getProperty(Config.AAF_CERT_IDS, null);
+                               if(cip!=null) {
+                                       certIDs = Split.split(',',cip);
+                               }
+                       }
+                       if(certIDs!=null && certs==null) {
+                               TimerTask cu = new CertUpdate(aafcon);
+                               cu.run(); // want this to run in this thread first...
+                               new Timer("AAF Identity Refresh Timer",true).scheduleAtFixedRate(cu, EIGHT_HOURS,EIGHT_HOURS);
+                       }
+               }
+       }
+
+       public static Set<String> trusted(String authMech) {
+               return trusted.get(authMech);
+       }
+       
+       public TaggedPrincipal identity(HttpServletRequest req, X509Certificate cert,   byte[] certBytes) throws CertificateException {
+               if(cert==null && certBytes==null)return null;
+               if(certBytes==null)certBytes = cert.getEncoded();
+               byte[] fingerprint = X509Taf.getFingerPrint(certBytes);
+               String id = certs.get(new ByteArrayHolder(fingerprint));
+               if(id!=null) { // Caller is Validated
+                       return new X509Principal(id,cert,certBytes);
+               }
+               return null;
+       }
+
+       private static class ByteArrayHolder implements Comparable<ByteArrayHolder> {
+               private byte[] ba;
+               public ByteArrayHolder(byte[] ba) {
+                       this.ba = ba;
+               }
+               public int compareTo(ByteArrayHolder b) {
+                       return Hash.compareTo(ba, b.ba);
+               }
+       }
+       
+       private class CertUpdate extends TimerTask {
+
+               private AAFCon<?> aafcon;
+               public CertUpdate(AAFCon<?> con) {
+                       aafcon = con;
+               }
+               
+               @Override
+               public void run() {
+                       try {
+                               TreeMap<ByteArrayHolder, String> newCertsMap = new TreeMap<ByteArrayHolder,String>();
+                               Map<String,Set<String>> newTrustMap = new TreeMap<String,Set<String>>();
+                               Set<String> userLookup = new HashSet<String>();
+                               for(String s : certIDs) {
+                                       userLookup.add(s);
+                               }
+                               for(String authMech : authMechanisms) {
+                                       Future<Users> fusr = aafcon.client(Config.AAF_DEFAULT_VERSION).read("/authz/users/perm/com.att.aaf.trust/"+authMech+"/authenticate", Users.class, aafcon.usersDF);
+                                       if(fusr.get(5000)) {
+                                               List<User> users = fusr.value.getUser();
+                                               if(users.isEmpty()) {
+                                                       aafcon.access.log(Level.WARN, "AAF Lookup-No IDs in Role com.att.aaf.trustForID <> "+authMech);
+                                               } else {
+                                                       aafcon.access.log(Level.INFO,"Loading Trust Authentication Info for",authMech);
+                                                       Set<String> hsUser = new HashSet<String>();
+                                                       for(User u : users) {
+                                                               userLookup.add(u.getId());
+                                                               hsUser.add(u.getId());
+                                                       }
+                                                       newTrustMap.put(authMech,hsUser);
+                                               }
+                                       } else {
+                                               aafcon.access.log(Level.WARN, "Could not get Users in Perm com.att.trust|tguard|authenticate",fusr.code(),fusr.body());
+                                       }
+                                       
+                               }
+                               
+                               for(String u : userLookup) {
+                                       Future<Certs> fc = aafcon.client(Config.AAF_DEFAULT_VERSION).read("/authn/cert/id/"+u, Certs.class, aafcon.certsDF);
+                                       XMLGregorianCalendar now = Chrono.timeStamp();
+                                       if(fc.get(5000)) {
+                                               List<Cert> certs = fc.value.getCert();
+                                               if(certs.isEmpty()) {
+                                                       aafcon.access.log(Level.WARN, "No Cert Associations for",u);
+                                               } else {
+                                                       for(Cert c : fc.value.getCert()) {
+                                                               XMLGregorianCalendar then =c.getExpires();
+                                                               if(then !=null && then.compare(now)>0) {
+                                                                       newCertsMap.put(new ByteArrayHolder(c.getFingerprint()), c.getId());
+                                                                       aafcon.access.log(Level.INIT,"Associating "+ c.getId() + " expiring " + Chrono.dateOnlyStamp(c.getExpires()) + " with " + c.getX500());
+                                                               }
+                                                       }
+                                               }
+                                       } else {
+                                               aafcon.access.log(Level.WARN, "Could not get Certificates for",u);
+                                       }
+                               }
+
+                               certs = newCertsMap;
+                               trusted = newTrustMap;
+                       } catch(Exception e) {
+                               aafcon.access.log(e, "Failure to update Certificate Identities from AAF");
+                       }
+               }
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java
new file mode 100644 (file)
index 0000000..0fb4d60
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.client;
+
+import java.io.PrintStream;
+
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.util.Vars;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Error;
+
+public class ErrMessage {
+       private RosettaDF<Error> errDF;
+       
+       public ErrMessage(RosettaEnv env) throws APIException {
+               errDF = env.newDataFactory(Error.class);
+       }
+
+       /**
+        * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with.
+        * 
+        * This code will create a meaningful string from this format. 
+        * 
+        * @param ps
+        * @param df
+        * @param r
+        * @throws APIException
+        */
+       public void printErr(PrintStream ps,  String attErrJson) throws APIException {
+               StringBuilder sb = new StringBuilder();
+               Error err = errDF.newData().in(TYPE.JSON).load(attErrJson).asObject();
+               ps.println(toMsg(sb,err));
+       }
+       
+       /**
+        * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with.
+        * 
+        * This code will create a meaningful string from this format. 
+        * 
+        * @param sb
+        * @param df
+        * @param r
+        * @throws APIException
+        */
+       public StringBuilder toMsg(StringBuilder sb,  String attErrJson) throws APIException {
+               return toMsg(sb,errDF.newData().in(TYPE.JSON).load(attErrJson).asObject());
+       }
+       
+       public StringBuilder toMsg(Future<?> future) {
+               return toMsg(new StringBuilder(),future);
+       }
+       
+       public StringBuilder toMsg(StringBuilder sb, Future<?> future) {
+               try {
+                       toMsg(sb,errDF.newData().in(TYPE.JSON).load(future.body()).asObject());
+               } catch(Exception e) {
+                       //just print what we can
+                       sb.append(future.code());
+                       sb.append(": ");
+                       sb.append(future.body());
+               }
+               return sb;
+       }
+
+       public StringBuilder toMsg(StringBuilder sb, Error err) {
+               sb.append(err.getMessageId());
+               sb.append(' ');
+               String[] vars = new String[err.getVariables().size()];
+               err.getVariables().toArray(vars);
+               Vars.convert(sb, err.getText(),vars);
+               return sb;
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java
new file mode 100644 (file)
index 0000000..31f60ee
--- /dev/null
@@ -0,0 +1,443 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.client;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.GregorianCalendar;
+
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Approval;
+import aaf.v2_0.Approvals;
+import aaf.v2_0.CredRequest;
+import aaf.v2_0.Keys;
+import aaf.v2_0.NsRequest;
+import aaf.v2_0.Nss;
+import aaf.v2_0.Nss.Ns;
+import aaf.v2_0.Perm;
+import aaf.v2_0.PermKey;
+import aaf.v2_0.PermRequest;
+import aaf.v2_0.Perms;
+import aaf.v2_0.Pkey;
+import aaf.v2_0.Request;
+import aaf.v2_0.Role;
+import aaf.v2_0.RoleKey;
+import aaf.v2_0.RolePermRequest;
+import aaf.v2_0.RoleRequest;
+import aaf.v2_0.Roles;
+import aaf.v2_0.UserRole;
+import aaf.v2_0.UserRoleRequest;
+import aaf.v2_0.UserRoles;
+import aaf.v2_0.Users;
+import aaf.v2_0.Users.User;
+
+public class Examples {
+       public static <C> String print(RosettaEnv env, String nameOrContentType, boolean optional) throws APIException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+               // Discover ClassName
+               String className = null;
+               String version = null;
+               TYPE type = TYPE.JSON; // default
+               if(nameOrContentType.startsWith("application/")) {
+                       for(String ct : nameOrContentType.split("\\s*,\\s*")) {
+                               for(String elem : ct.split("\\s*;\\s*")) {
+                                       if(elem.endsWith("+json")) {
+                                               type = TYPE.JSON;
+                                               className = elem.substring(elem.indexOf('/')+1, elem.length()-5);
+                                       } else if(elem.endsWith("+xml")) {
+                                               type = TYPE.XML;
+                                               className = elem.substring(elem.indexOf('/')+1, elem.length()-4);
+                                       } else if(elem.startsWith("version=")) {
+                                               version = elem.substring(8);
+                                       }
+                               }
+                               if(className!=null && version!=null)break;
+                       }
+                       if(className==null) {
+                               throw new APIException(nameOrContentType + " does not contain Class Information");
+                       }
+               } else {
+                       className = nameOrContentType;
+               }
+               
+               // No Void.class in aaf.v2_0 package causing errors when trying to use a newVoidv2_0
+               // method similar to others in this class. This makes it work, but is it right?
+               if ("Void".equals(className)) return "";
+                               
+               if("1.1".equals(version)) {
+                       version = "v1_0";
+               } else if(version!=null) {
+                       version = "v" + version.replace('.', '_');
+               } else {
+                       version = "v2_0";
+               }
+               
+               Class<?> cls;
+               try {
+                       cls = Examples.class.getClassLoader().loadClass("aaf."+version+'.'+className);
+               } catch (ClassNotFoundException e) {
+                       throw new APIException(e);
+               }
+               
+               Method meth;
+               try {
+                       meth = Examples.class.getDeclaredMethod("new"+cls.getSimpleName()+version,boolean.class);
+               } catch (Exception e) {
+                       throw new APIException("ERROR: " + cls.getName() + " does not have an Example in Code.  Request from AAF Developers");
+               }
+               
+               RosettaDF<C> df = env.newDataFactory(cls);
+               df.option(Data.PRETTY);
+               
+               Object data = meth.invoke(null,optional);
+               
+               @SuppressWarnings("unchecked")
+               String rv = df.newData().load((C)data).out(type).asString();
+//             Object obj = df.newData().in(type).load(rv).asObject();
+               return rv;
+       }
+       
+       /*
+        *  Set Base Class Request (easier than coding over and over)
+        */
+       private static void setOptional(Request req) {
+               GregorianCalendar gc = new GregorianCalendar();
+               req.setStart(Chrono.timeStamp(gc));
+               gc.add(GregorianCalendar.MONTH, 6);
+               req.setEnd(Chrono.timeStamp(gc));
+//             req.setForce("false");
+               
+       }
+       
+       @SuppressWarnings("unused")
+       private static Request newRequestv2_0(boolean optional) {
+               Request r = new Request();
+               setOptional(r);
+               return r;
+       }
+       @SuppressWarnings("unused")
+       private static RolePermRequest newRolePermRequestv2_0(boolean optional) {
+               RolePermRequest rpr = new RolePermRequest();
+               Pkey pkey = new Pkey();
+               pkey.setType("org.osaaf.myns.mytype");
+               pkey.setInstance("myInstance");
+               pkey.setAction("myAction");
+               rpr.setPerm(pkey);
+               rpr.setRole("org.osaaf.myns.myrole");
+               if(optional)setOptional(rpr);
+               return rpr;
+       }
+       
+       @SuppressWarnings("unused")
+       private static Roles newRolesv2_0(boolean optional) {
+               Role r;
+               Pkey p;
+               Roles rs = new Roles();
+               rs.getRole().add(r = new Role());
+               r.setName("org.osaaf.myns.myRole");
+               r.getPerms().add(p = new Pkey());
+               p.setType("org.osaaf.myns.myType");
+               p.setInstance("myInstance");
+               p.setAction("myAction");
+               
+               r.getPerms().add(p = new Pkey());
+               p.setType("org.osaaf.myns.myType");
+               p.setInstance("myInstance");
+               p.setAction("myOtherAction");
+               
+               rs.getRole().add(r = new Role());
+               r.setName("org.osaaf.myns.myOtherRole");
+               r.getPerms().add(p = new Pkey());
+               p.setType("org.osaaf.myns.myOtherType");
+               p.setInstance("myInstance");
+               p.setAction("myAction");
+               
+               r.getPerms().add(p = new Pkey());
+               p.setType("org.osaaf.myns.myOthertype");
+               p.setInstance("myInstance");
+               p.setAction("myOtherAction");
+
+               return rs;
+       }
+       
+       
+       @SuppressWarnings("unused")
+       private static PermRequest newPermRequestv2_0(boolean optional) {
+               PermRequest pr = new PermRequest();
+               pr.setType("org.osaaf.myns.myType");
+               pr.setInstance("myInstance");
+               pr.setAction("myAction");
+               if(optional) {
+                       pr.setDescription("Short and meaningful verbiage about the Permission");
+                       
+                       setOptional(pr);
+               }
+               return pr;
+       }
+       
+       @SuppressWarnings("unused")
+       private static Perm newPermv2_0(boolean optional) {
+               Perm pr = new Perm();
+               pr.setType("org.osaaf.myns.myType");
+               pr.setInstance("myInstance");
+               pr.setAction("myAction");
+               pr.getRoles().add("org.osaaf.aaf.myRole");
+               pr.getRoles().add("org.osaaf.aaf.myRole2");
+               pr.setDescription("This is my description, and I'm sticking with it");
+               if(optional) {
+                       pr.setDescription("Short and meaningful verbiage about the Permission");
+               }
+               return pr;
+       }
+
+
+       @SuppressWarnings("unused")
+       private static PermKey newPermKeyv2_0(boolean optional) {
+               PermKey pr = new PermKey();
+               pr.setType("org.osaaf.myns.myType");
+               pr.setInstance("myInstance");
+               pr.setAction("myAction");
+               return pr;
+       }
+       
+       @SuppressWarnings("unused")
+       private static Perms newPermsv2_0(boolean optional) {
+               Perms perms = new Perms();
+               Perm p;
+               perms.getPerm().add(p=new Perm());
+               p.setType("org.osaaf.myns.myType");
+               p.setInstance("myInstance");
+               p.setAction("myAction");
+               p.getRoles().add("org.osaaf.myns.myRole");
+               p.getRoles().add("org.osaaf.myns.myRole2");
+               
+
+               perms.getPerm().add(p=new Perm());
+               p.setType("org.osaaf.myns.myOtherType");
+               p.setInstance("myInstance");
+               p.setAction("myOtherAction");
+               p.getRoles().add("org.osaaf.myns.myRole");
+               p.getRoles().add("org.osaaf.myns.myRole2");
+
+               return perms;
+               
+       }
+       
+       @SuppressWarnings("unused")
+       private static UserRoleRequest newUserRoleRequestv2_0(boolean optional) {
+               UserRoleRequest urr = new UserRoleRequest();
+               urr.setRole("org.osaaf.myns.myRole");
+               urr.setUser("ab1234@csp.att.com");
+               if(optional) setOptional(urr);
+               return urr;
+       }
+       
+       @SuppressWarnings("unused")
+       private static NsRequest newNsRequestv2_0(boolean optional) {
+               NsRequest nr = new NsRequest();
+               nr.setName("org.osaaf.myns");
+               nr.getResponsible().add("ab1234@csp.att.com");
+               nr.getResponsible().add("cd5678@csp.att.com");
+               nr.getAdmin().add("zy9876@csp.att.com");
+               nr.getAdmin().add("xw5432@csp.att.com");                
+               if(optional) {
+                       nr.setDescription("This is my Namespace to set up");
+                       nr.setType("APP");
+                       setOptional(nr);
+               }
+               return nr;
+       }
+       
+       
+       @SuppressWarnings("unused")
+       private static Nss newNssv2_0(boolean optional) {
+               Ns ns;
+               
+               Nss nss = new Nss();
+               nss.getNs().add(ns = new Nss.Ns());
+               ns.setName("org.osaaf.myns");
+               ns.getResponsible().add("ab1234@csp.att.com");
+               ns.getResponsible().add("cd5678@csp.att.com");
+               ns.getAdmin().add("zy9876@csp.att.com");
+               ns.getAdmin().add("xw5432@csp.att.com");
+               ns.setDescription("This is my Namespace to set up");
+               
+               nss.getNs().add(ns = new Nss.Ns());
+               ns.setName("org.osaaf.myOtherNs");
+               ns.getResponsible().add("ab1234@csp.att.com");
+               ns.getResponsible().add("cd5678@csp.att.com");
+               ns.getAdmin().add("zy9876@csp.att.com");
+               ns.getAdmin().add("xw5432@csp.att.com");                
+                       
+               return nss;
+       }
+       @SuppressWarnings("unused")
+       private static RoleRequest newRoleRequestv2_0(boolean optional) {
+               RoleRequest rr = new RoleRequest();
+               rr.setName("org.osaaf.myns.myRole");
+               if(optional) {
+                       rr.setDescription("This is my Role");
+                       setOptional(rr);
+               }
+               return rr;
+       }
+
+       @SuppressWarnings("unused")
+       private static CredRequest newCredRequestv2_0(boolean optional) {
+               CredRequest cr = new CredRequest();
+               cr.setId("myID@fully.qualified.domain");
+               if(optional) {
+                       cr.setType(2);
+                       cr.setEntry("0x125AB256344CE");
+               } else {
+                       cr.setPassword("This is my provisioned password");
+               }
+
+               return cr;
+       }
+       
+       @SuppressWarnings("unused")
+       private static Users newUsersv2_0(boolean optional) {
+               User user;
+       
+               Users users = new Users();
+               users.getUser().add(user = new Users.User());
+               user.setId("ab1234@csp.att.com");       
+               GregorianCalendar gc = new GregorianCalendar();
+               user.setExpires(Chrono.timeStamp(gc));
+               
+               users.getUser().add(user = new Users.User());
+               user.setId("zy9876@csp.att.com");       
+               user.setExpires(Chrono.timeStamp(gc));  
+                       
+               return users;
+       }
+
+       @SuppressWarnings("unused")
+       private static Role newRolev2_0(boolean optional) {
+               Role r = new Role();
+               Pkey p;
+               r.setName("org.osaaf.myns.myRole");
+               r.getPerms().add(p = new Pkey());
+               p.setType("org.osaaf.myns.myType");
+               p.setInstance("myInstance");
+               p.setAction("myAction");
+
+        return r;
+    }
+
+       @SuppressWarnings("unused")
+       private static RoleKey newRoleKeyv2_0(boolean optional) {
+               RoleKey r = new RoleKey();
+               Pkey p;
+               r.setName("org.osaaf.myns.myRole");
+        return r;
+    }
+
+       @SuppressWarnings("unused")
+       private static Keys newKeysv2_0(boolean optional) {
+               Keys ks = new Keys();
+               ks.getKey().add("Reponse 1");
+               ks.getKey().add("Response 2");
+        return ks;
+    }
+
+       @SuppressWarnings("unused")
+       private static UserRoles newUserRolesv2_0(boolean optional) {
+               UserRoles urs = new UserRoles();
+               UserRole ur = new UserRole();
+               ur.setUser("xy1234");
+               ur.setRole("com.test.myapp.myRole");
+               ur.setExpires(Chrono.timeStamp());
+               urs.getUserRole().add(ur);
+               
+               ur = new UserRole();
+               ur.setUser("yx4321");
+               ur.setRole("com.test.yourapp.yourRole");
+               ur.setExpires(Chrono.timeStamp());
+               urs.getUserRole().add(ur);
+        return urs;
+    }
+
+
+       @SuppressWarnings("unused")
+       private static Approvals newApprovalsv2_0(boolean optional) {
+               Approvals as = new Approvals();
+               Approval a = new Approval();
+               a.setApprover("MyApprover");
+               a.setId("MyID");
+               a.setMemo("My memo (and then some)");
+               a.setOperation("MyOperation");
+               a.setStatus("MyStatus");
+               a.setTicket("MyTicket");
+               a.setType("MyType");
+               a.setUpdated(Chrono.timeStamp());
+               a.setUser("MyUser");
+               as.getApprovals().add(a);
+               a = new Approval();
+               a.setApprover("MyApprover2");
+               a.setId("MyID2");
+               a.setMemo("My memo (and then some)2");
+               a.setOperation("MyOperation2");
+               a.setStatus("MyStatus2");
+               a.setTicket("MyTicket2");
+               a.setType("MyType2");
+               a.setUpdated(Chrono.timeStamp());
+               a.setUser("MyUser2");
+               as.getApprovals().add(a);
+        return as;
+    }
+
+       @SuppressWarnings("unused")
+       private static Approval newApprovalv2_0(boolean optional) {
+               Approval a = new Approval();
+               a.setApprover("MyApprover");
+               a.setId("MyID");
+               a.setMemo("My memo (and then some)");
+               a.setOperation("MyOperation");
+               a.setStatus("MyStatus");
+               a.setTicket("MyTicket");
+               a.setType("MyType");
+               a.setUpdated(Chrono.timeStamp());
+               a.setUser("MyUser");
+        return a;
+    }
+
+       
+
+       @SuppressWarnings("unused")
+       private static aaf.v2_0.Error newErrorv2_0(boolean optional) {
+               aaf.v2_0.Error err = new aaf.v2_0.Error();
+               err.setMessageId("SVC1403");
+               err.setText("MyText %s, %s: The last three digits are usually the HTTP Code");
+               err.getVariables().add("Variable 1");
+               err.getVariables().add("Variable 2");
+               return err;
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java
new file mode 100644 (file)
index 0000000..5ceb082
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.marshal;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.onap.aaf.misc.rosetta.marshal.FieldDateTime;
+import org.onap.aaf.misc.rosetta.marshal.FieldHexBinary;
+import org.onap.aaf.misc.rosetta.marshal.FieldString;
+import org.onap.aaf.misc.rosetta.marshal.ObjMarshal;
+
+import aaf.v2_0.Certs.Cert;
+
+public class CertMarshal extends ObjMarshal<Cert> {
+       public CertMarshal() {
+               add(new FieldHexBinary<Cert>("fingerprint") {
+                       @Override
+                       protected byte[] data(Cert t) {
+                               return t.getFingerprint();
+                       }
+               });
+
+               add(new FieldString<Cert>("id") {
+                       @Override
+                       protected String data(Cert t) {
+                               return t.getId();
+                       }
+               });
+
+               add(new FieldString<Cert>("x500") {
+                       @Override
+                       protected String data(Cert t) {
+                               return t.getX500();
+                       }
+               });
+               
+               add(new FieldDateTime<Cert>("expires") {
+                       @Override
+                       protected XMLGregorianCalendar data(Cert t) {
+                               return t.getExpires();
+                       }
+               });
+
+
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java
new file mode 100644 (file)
index 0000000..c6e2840
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.marshal;
+
+import java.util.List;
+
+import org.onap.aaf.misc.rosetta.marshal.ObjArray;
+import org.onap.aaf.misc.rosetta.marshal.ObjMarshal;
+
+import aaf.v2_0.Certs;
+import aaf.v2_0.Certs.Cert;
+
+public class CertsMarshal extends ObjMarshal<Certs> {
+
+       public CertsMarshal() {
+               add(new ObjArray<Certs,Cert>("cert",new CertMarshal()) {
+                       @Override
+                       protected List<Cert> data(Certs t) {
+                               return t.getCert();
+                       }
+               });     
+       }
+
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java
new file mode 100644 (file)
index 0000000..3c970bc
--- /dev/null
@@ -0,0 +1,169 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.lur.ConfigPrincipal;
+
+public class AAFAuthn<CLIENT> extends AbsUserCache<AAFPermission> {
+       private AAFCon<CLIENT> con;
+       private String realm;
+       
+       /**
+        * Configure with Standard AAF properties, Stand alone
+        * @param con
+        * @throws Exception ..
+        */
+       // Package on purpose
+       AAFAuthn(AAFCon<CLIENT> con) throws Exception {
+               super(con.access,con.cleanInterval,con.highCount,con.usageRefreshTriggerCount);
+               this.con = con;
+       }
+
+       /**
+        * Configure with Standard AAF properties, but share the Cache (with AAF Lur)
+        * @param con
+        * @throws Exception 
+        */
+       // Package on purpose
+       AAFAuthn(AAFCon<CLIENT> con, AbsUserCache<AAFPermission> cache) {
+               super(cache);
+               this.con = con;
+       }
+       
+       /**
+        * Return Native Realm of AAF Instance.
+        * 
+        * @return
+        */
+       public String getRealm() {
+               return realm;
+       }
+
+       /**
+        * Returns null if ok, or an Error String;
+        * 
+        * Convenience function.  Passes "null" for State object
+        */
+       public String validate(String user, String password) throws IOException, CadiException {
+               return validate(user,password,null);
+       }
+       
+       /**
+        * Returns null if ok, or an Error String;
+        * 
+        * For State Object, you may put in HTTPServletRequest or AuthzTrans, if available.  Otherwise,
+        * leave null
+        * 
+        * @param user
+        * @param password
+        * @return
+        * @throws IOException 
+        * @throws CadiException 
+        * @throws Exception
+        */
+       public String validate(String user, String password, Object state) throws IOException, CadiException {
+               password = access.decrypt(password, false);
+               byte[] bytes = password.getBytes();
+               User<AAFPermission> usr = getUser(user,bytes);
+
+               if(usr != null && !usr.permExpired()) {
+                       if(usr.principal==null) {
+                               return "User already denied";
+                       } else {
+                               return null; // good
+                       }
+               }
+
+               AAFCachedPrincipal cp = new AAFCachedPrincipal(this,con.app, user, bytes, con.cleanInterval);
+               // Since I've relocated the Validation piece in the Principal, just revalidate, then do Switch
+               // Statement
+               switch(cp.revalidate(state)) {
+                       case REVALIDATED:
+                               if(usr!=null) {
+                                       usr.principal = cp;
+                               } else {
+                                       addUser(new User<AAFPermission>(cp,con.timeout));
+                               }
+                               return null;
+                       case INACCESSIBLE:
+                               return "AAF Inaccessible";
+                       case UNVALIDATED:
+                               addUser(new User<AAFPermission>(user,bytes,con.timeout));
+                               return "User/Pass combo invalid for " + user;
+                       case DENIED:
+                               return "AAF denies API for " + user;
+                       default: 
+                               return "AAFAuthn doesn't handle Principal " + user;
+               }
+       }
+       
+       private class AAFCachedPrincipal extends ConfigPrincipal implements CachedPrincipal {
+               private long expires,timeToLive;
+
+               public AAFCachedPrincipal(AAFAuthn<?> aaf, String app, String name, byte[] pass, int timeToLive) {
+                       super(name,pass);
+                       this.timeToLive = timeToLive;
+                       expires = timeToLive + System.currentTimeMillis();
+               }
+
+               public Resp revalidate(Object state) {
+                       try {
+                               Miss missed = missed(getName(),getCred());
+                               if(missed==null || missed.mayContinue()) {
+                                       Rcli<CLIENT> client = con.client(Config.AAF_DEFAULT_VERSION).forUser(con.basicAuth(getName(), new String(getCred())));
+                                       Future<String> fp = client.read(
+                                                       "/authn/basicAuth",
+                                                       "text/plain"
+                                                       );
+                                       if(fp.get(con.timeout)) {
+                                               expires = System.currentTimeMillis() + timeToLive;
+                                               addUser(new User<AAFPermission>(this, expires));
+                                               return Resp.REVALIDATED;
+                                       } else {
+                                               addMiss(getName(), getCred());
+                                               return Resp.UNVALIDATED;
+                                       }
+                               } else {
+                                       return Resp.UNVALIDATED;
+                               }
+                       } catch (Exception e) {
+                               con.access.log(e);
+                               return Resp.INACCESSIBLE;
+                       }
+               }
+
+               public long expires() {
+                       return expires;
+               }
+       };
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java
new file mode 100644 (file)
index 0000000..70b3e76
--- /dev/null
@@ -0,0 +1,371 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Connector;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.marshal.CertsMarshal;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.cadi.util.Vars;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Certs;
+import aaf.v2_0.Error;
+import aaf.v2_0.Perms;
+import aaf.v2_0.Users;
+
+public abstract class AAFCon<CLIENT> implements Connector {
+       final public Access access;
+       // Package access
+       final public int timeout, cleanInterval, connTimeout;
+       final public int highCount, userExpires, usageRefreshTriggerCount;
+       private Map<String,Rcli<CLIENT>> clients = new ConcurrentHashMap<String,Rcli<CLIENT>>();
+       final public RosettaDF<Perms> permsDF;
+       final public RosettaDF<Certs> certsDF;
+       final public RosettaDF<Users> usersDF;
+       final public RosettaDF<Error> errDF;
+       private String realm;
+       public final String app;
+       protected SecuritySetter<CLIENT> ss;
+       protected SecurityInfoC<CLIENT> si;
+
+       private AAFLurPerm lur;
+
+       final public RosettaEnv env;
+       protected abstract URI initURI();
+       protected abstract void setInitURI(String uriString) throws CadiException;
+
+       /**
+        * Use this call to get the appropriate client based on configuration (HTTP, future)
+        * 
+        * @param apiVersion
+        * @return
+        * @throws CadiException
+        */
+       public Rcli<CLIENT> client(String apiVersion) throws CadiException {
+               Rcli<CLIENT> client = clients.get(apiVersion);
+               if(client==null) {
+                       client = rclient(initURI(),ss);
+                       client.apiVersion(apiVersion)
+                                 .readTimeout(connTimeout);
+                       clients.put(apiVersion, client);
+               } 
+               return client;
+       }
+
+       public Rcli<CLIENT> client(URI uri) throws CadiException {
+               return rclient(uri,ss).readTimeout(connTimeout);
+       }
+       
+       /**
+        * Use this API when you have permission to have your call act as the end client's ID.
+        * 
+        *  Your calls will get 403 errors if you do not have this permission.  it is a special setup, rarely given.
+        * 
+        * @param apiVersion
+        * @param req
+        * @return
+        * @throws CadiException
+        */
+       public Rcli<CLIENT> clientAs(String apiVersion, TaggedPrincipal p) throws CadiException {
+               Rcli<CLIENT> cl = client(apiVersion);
+               return cl.forUser(transferSS(p));
+       }
+       
+       protected AAFCon(AAFCon<CLIENT> copy) {
+               access = copy.access;
+               timeout = copy.timeout;
+               cleanInterval = copy.cleanInterval;
+               connTimeout = copy.connTimeout;
+               highCount = copy.highCount;
+               userExpires = copy.userExpires;
+               usageRefreshTriggerCount = copy.usageRefreshTriggerCount;
+               permsDF = copy.permsDF;
+               certsDF = copy.certsDF;
+               usersDF = copy.usersDF;
+               errDF = copy.errDF;
+               app = copy.app;
+               ss = copy.ss;
+               si = copy.si;
+               env = copy.env;
+               realm = copy.realm;
+       }
+       
+       protected AAFCon(Access access, String tag, SecurityInfoC<CLIENT> si) throws CadiException{
+               if(tag==null) {
+                       throw new CadiException("AAFCon cannot be constructed without a property tag or URL");
+               } else {
+                       String str = access.getProperty(tag,null);
+                       if(str==null) {
+                               if(tag.contains("://")) { // assume a URL
+                                       str = tag;
+                               } else {
+                                       throw new CadiException("A URL or " + tag + " property is required.");
+                               }
+                       }
+                       setInitURI(str);
+               }
+               try {
+                       this.access = access;
+                       this.si = si;
+                       this.ss = si.defSS;
+                       if(ss.getID().equals(SecurityInfoC.DEF_ID)) { // it's the Preliminary SS, try to get a better one
+                               String mechid = access.getProperty(Config.AAF_APPID, null);
+                               if(mechid==null) {
+                                       mechid=access.getProperty(Config.OAUTH_CLIENT_ID,null);
+                               }
+                               String encpass = access.getProperty(Config.AAF_APPPASS, null);
+                               if(encpass==null) {
+                                       encpass = access.getProperty(Config.OAUTH_CLIENT_SECRET,null);
+                               }
+                               if(encpass==null) {
+                                       String alias = access.getProperty(Config.CADI_ALIAS, mechid);
+                                       if(alias==null) {
+                                               access.printf(Access.Level.WARN,"%s, %s or %s required before use.", Config.CADI_ALIAS, Config.AAF_APPID, Config.OAUTH_CLIENT_ID);
+                                               set(si.defSS);
+                                       } else {
+                                               set(si.defSS=x509Alias(alias));
+                                       }
+                               } else {
+                                       if(mechid!=null && encpass !=null) {
+                                               set(si.defSS=basicAuth(mechid, encpass));
+                                       } else {
+                                               set(si.defSS=new SecuritySetter<CLIENT>() {
+                                                       
+                                                       @Override
+                                                       public String getID() {
+                                                               return "";
+                                                       }
+                       
+                                                       @Override
+                                                       public void setSecurity(CLIENT client) throws CadiException {
+                                                               throw new CadiException("AAFCon has not been initialized with Credentials (SecuritySetter)");
+                                                       }
+
+                                                       @Override
+                                                       public int setLastResponse(int respCode) {
+                                                               return 0;
+                                                       }
+                                               });
+                                       }
+                               }
+                       }
+                       
+                       timeout = Integer.parseInt(access.getProperty(Config.AAF_CALL_TIMEOUT, Config.AAF_CALL_TIMEOUT_DEF));
+                       cleanInterval = Integer.parseInt(access.getProperty(Config.AAF_CLEAN_INTERVAL, Config.AAF_CLEAN_INTERVAL_DEF));
+                       highCount = Integer.parseInt(access.getProperty(Config.AAF_HIGH_COUNT, Config.AAF_HIGH_COUNT_DEF).trim());
+                       connTimeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF).trim());
+                       userExpires = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim());
+                       usageRefreshTriggerCount = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim())-1; // zero based
+       
+                       app=FQI.reverseDomain(ss.getID());
+                       //TODO Get Realm from AAF
+                       realm="csp.att.com";
+       
+                       env = new RosettaEnv();
+                       permsDF = env.newDataFactory(Perms.class);
+                       usersDF = env.newDataFactory(Users.class);
+                       certsDF = env.newDataFactory(Certs.class);
+                       certsDF.rootMarshal(new CertsMarshal()); // Speedier Marshaling
+                       errDF = env.newDataFactory(Error.class);
+               } catch (APIException e) {
+                       throw new CadiException("AAFCon cannot be configured",e);
+               }
+       }
+       
+       public RosettaEnv env() {
+               return env;
+       }
+       
+       /**
+        * Return the backing AAFCon, if there is a Lur Setup that is AAF.
+        * 
+        * If there is no AAFLur setup, it will return "null"
+        * @param servletRequest
+        * @return
+        */
+       public static final AAFCon<?> obtain(Object servletRequest) {
+               if(servletRequest instanceof CadiWrap) {
+                       Lur lur = ((CadiWrap)servletRequest).getLur();
+                       if(lur != null) {
+                               if(lur instanceof EpiLur) {
+                                       AbsAAFLur<?> aal = (AbsAAFLur<?>) ((EpiLur)lur).subLur(AbsAAFLur.class);
+                                       if(aal!=null) {
+                                               return aal.aaf;
+                                       }
+                               } else {
+                                       if(lur instanceof AbsAAFLur) {
+                                               return ((AbsAAFLur<?>)lur).aaf;
+                                       }
+                               }
+                       }
+               }
+               return null;
+       }
+       
+       public abstract AAFCon<CLIENT> clone(String url) throws CadiException, LocatorException;
+       
+       public AAFAuthn<CLIENT> newAuthn() throws APIException {
+               try {
+                       return new AAFAuthn<CLIENT>(this);
+               } catch (APIException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new APIException(e);
+               }
+       }
+
+       public AAFAuthn<CLIENT> newAuthn(AbsUserCache<AAFPermission> c) {
+               return new AAFAuthn<CLIENT>(this,c);
+       }
+
+       public AAFLurPerm newLur() throws CadiException {
+               try {
+                       if(lur==null) {
+                               return (lur =  new AAFLurPerm(this));
+                       } else {
+                               return new AAFLurPerm(this,lur);
+                       }
+               } catch (CadiException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+       }
+       
+       public AAFLurPerm newLur(AbsUserCache<AAFPermission> c) throws APIException {
+               try {
+                       return new AAFLurPerm(this,c);
+               } catch (APIException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new APIException(e);
+               }
+       }
+
+       protected abstract Rcli<CLIENT> rclient(URI uri, SecuritySetter<CLIENT> ss) throws CadiException;
+       
+       public abstract Rcli<CLIENT> rclient(Locator<URI> loc, SecuritySetter<CLIENT> ss) throws CadiException;
+
+       public Rcli<CLIENT> client(Locator<URI> locator) throws CadiException {
+               return rclient(locator,ss);
+       }
+       
+       public abstract<RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
+
+       public abstract<RET> RET bestForUser(GetSetter get, Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
+
+       public abstract SecuritySetter<CLIENT> basicAuth(String user, String password) throws CadiException;
+       
+       public abstract SecuritySetter<CLIENT> transferSS(TaggedPrincipal principal) throws CadiException;
+       
+       public abstract SecuritySetter<CLIENT> basicAuthSS(BasicPrincipal principal) throws CadiException;
+       
+       public abstract SecuritySetter<CLIENT> tokenSS(final String client_id, final String accessToken) throws CadiException;
+       
+       public abstract SecuritySetter<CLIENT> x509Alias(String alias) throws APIException, CadiException;
+       
+
+       public String getRealm() {
+               return realm;
+
+       }
+       
+       /**
+        * This interface allows the AAFCon, even though generic, to pass in correctly typed values based on the above SS commands.
+        * @author Jonathan
+        *
+        */
+       public interface GetSetter {
+               public<CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException;
+       }
+
+       public SecuritySetter<CLIENT> set(final SecuritySetter<CLIENT> ss) {
+               this.ss = ss;
+               for(Rcli<CLIENT> client : clients.values()) {
+                       client.setSecuritySetter(ss);
+               }
+               return ss;
+       }
+       
+       public SecurityInfoC<CLIENT> securityInfo() {
+               return si;
+       }
+
+       public String defID() {
+               if(ss!=null) {
+                       return ss.getID();
+               }
+               return "unknown";
+       }
+       
+       public void invalidate() throws CadiException {
+               for(Rcli<CLIENT> client : clients.values()) {
+                       client.invalidate();
+               }
+               clients.clear();
+       }
+
+       public String readableErrMsg(Future<?> f) {
+               String text = f.body();
+               if(text==null || text.length()==0) {
+                       text = f.code() + ": **No Message**";
+               } else if(text.contains("%")) {
+                       try {
+                               Error err = errDF.newData().in(TYPE.JSON).load(f.body()).asObject();
+                               return Vars.convert(err.getText(),err.getVariables());
+                       } catch (APIException e){
+                               // just return the body below
+                       }
+               }
+               return text;
+       }
+       
+       public static AAFCon<?> newInstance(PropAccess pa) throws APIException, CadiException, LocatorException {
+               // Potentially add plugin for other kinds of Access
+               return new AAFConHttp(pa);
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java
new file mode 100644 (file)
index 0000000..6d54e36
--- /dev/null
@@ -0,0 +1,229 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.AbsTransferSS;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HBasicAuthSS;
+import org.onap.aaf.cadi.http.HMangr;
+import org.onap.aaf.cadi.http.HRcli;
+import org.onap.aaf.cadi.http.HTokenSS;
+import org.onap.aaf.cadi.http.HTransferSS;
+import org.onap.aaf.cadi.http.HX509SS;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.misc.env.APIException;
+
+public class AAFConHttp extends AAFCon<HttpURLConnection> {
+       private final HMangr hman;
+
+       public AAFConHttp(Access access) throws APIException, CadiException, LocatorException {
+               super(access,Config.AAF_URL,SecurityInfoC.instance(access, HttpURLConnection.class));
+               bestSS(si);
+               hman = new HMangr(access,Config.loadLocator(si, access.getProperty(Config.AAF_URL,null)));
+       }
+
+       public static SecuritySetter<HttpURLConnection> bestSS(SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException {
+               Access access = si.access;
+               String s;
+               if((s = access.getProperty(Config.CADI_ALIAS, null))!=null) {
+                       return new HX509SS(s,si,true);
+               } else if((s = access.getProperty(Config.AAF_APPID, null))!=null){
+                       try {
+                               return new HBasicAuthSS(si,true);
+                       } catch (IOException /*| GeneralSecurityException*/ e) {
+                               throw new CadiException(e);
+                       }
+               }
+               return null;
+       }
+
+       public AAFConHttp(Access access, String tag) throws APIException, CadiException, LocatorException {
+               super(access,tag,SecurityInfoC.instance(access, HttpURLConnection.class));
+               bestSS(si);
+               hman = new HMangr(access,Config.loadLocator(si, access.getProperty(tag,tag/*try the content itself*/)));
+       }
+
+       public AAFConHttp(Access access, String urlTag, SecurityInfoC<HttpURLConnection> si) throws CadiException, APIException, LocatorException {
+               super(access,urlTag,si);
+               bestSS(si);
+               hman = new HMangr(access,Config.loadLocator(si, access.getProperty(urlTag,null)));
+       }
+
+       public AAFConHttp(Access access, Locator<URI> locator) throws CadiException, LocatorException, APIException {
+               super(access,Config.AAF_URL,SecurityInfoC.instance(access, HttpURLConnection.class));
+               bestSS(si);
+               hman = new HMangr(access,locator);
+       }
+
+       public AAFConHttp(Access access, Locator<URI> locator, SecurityInfoC<HttpURLConnection> si) throws CadiException, LocatorException {
+               super(access,Config.AAF_URL,si);
+               hman = new HMangr(access,locator);
+       }
+
+       public AAFConHttp(Access access, Locator<URI> locator, SecurityInfoC<HttpURLConnection> si, String tag) throws CadiException, LocatorException {
+               super(access,tag,si);
+               hman = new HMangr(access, locator);
+       }
+       
+       private AAFConHttp(AAFCon<HttpURLConnection> aafcon, String url) throws LocatorException {
+               super(aafcon);
+               hman = new HMangr(aafcon.access,Config.loadLocator(si, url));
+       }
+
+       @Override
+       public AAFCon<HttpURLConnection> clone(String url) throws LocatorException {
+               return new AAFConHttp(this,url);
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#basicAuth(java.lang.String, java.lang.String)
+        */
+       @Override
+       public SecuritySetter<HttpURLConnection> basicAuth(String user, String password) throws CadiException {
+               if(password.startsWith("enc:")) {
+                       try {
+                               password = access.decrypt(password, true);
+                       } catch (IOException e) {
+                               throw new CadiException("Error decrypting password",e);
+                       }
+               }
+               try {
+                       return new HBasicAuthSS(si,user,password);
+               } catch (IOException e) {
+                       throw new CadiException("Error creating HBasicAuthSS",e);
+               }
+       }
+
+       public SecuritySetter<HttpURLConnection> x509Alias(String alias) throws APIException, CadiException {
+               try {
+                       return set(new HX509SS(alias,si));
+               } catch (Exception e) {
+                       throw new CadiException("Error creating X509SS",e);
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#rclient(java.net.URI, org.onap.aaf.cadi.SecuritySetter)
+        */
+       @Override
+       protected Rcli<HttpURLConnection> rclient(URI ignoredURI, SecuritySetter<HttpURLConnection> ss) throws CadiException {
+               if(hman.loc==null) {
+                       throw new CadiException("No Locator set in AAFConHttp"); 
+               }
+               try {
+                       return new HRcli(hman, hman.loc.best() ,ss);
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+       }
+       
+       @Override
+       public Rcli<HttpURLConnection> rclient(Locator<URI> loc, SecuritySetter<HttpURLConnection> ss) throws CadiException {
+               try {
+                       HMangr newHMan = new HMangr(access, loc);
+                       return new HRcli(newHMan,newHMan.loc.best(),ss);
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+       }
+       @Override
+       public AbsTransferSS<HttpURLConnection> transferSS(TaggedPrincipal principal) throws CadiException {
+               return new HTransferSS(principal, app,si);
+       }
+       
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#basicAuthSS(java.security.Principal)
+        */
+       @Override
+       public SecuritySetter<HttpURLConnection> basicAuthSS(BasicPrincipal principal) throws CadiException {
+               try {
+                       return new HBasicAuthSS(principal,si);
+               } catch (IOException e) {
+                       throw new CadiException("Error creating HBasicAuthSS",e);
+               }
+       }
+
+       @Override
+       public SecuritySetter<HttpURLConnection> tokenSS(final String client_id, final String accessToken) throws CadiException {
+               try {
+                       return new HTokenSS(si, client_id, accessToken);
+               } catch (IOException e) {
+                       throw new CadiException(e);
+               }
+       }
+
+       public HMangr hman() {
+               return hman;
+       }
+
+       @Override
+       public <RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+               return hman.best(ss, (Retryable<RET>)retryable);
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#bestForUser(org.onap.aaf.cadi.SecuritySetter, org.onap.aaf.cadi.client.Retryable)
+        */
+       @Override
+       public <RET> RET bestForUser(GetSetter getSetter, Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+               return hman.best(getSetter.get(this), (Retryable<RET>)retryable);
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#initURI()
+        */
+       @Override
+       protected URI initURI() {
+               try {
+                       Item item = hman.loc.best();
+                       if(item!=null) {
+                               return hman.loc.get(item);
+                       }
+               } catch (LocatorException e) {
+                       access.log(e, "Error in AAFConHttp obtaining initial URI");
+               }
+               return null;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#setInitURI(java.lang.String)
+        */
+       @Override
+       protected void setInitURI(String uriString) throws CadiException {
+               // Using Locator, not URLString, which is mostly for DME2
+       }
+       
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java
new file mode 100644 (file)
index 0000000..4f60eda
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HClient;
+import org.onap.aaf.cadi.util.Split;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.impl.BasicTrans;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import locate.v1_0.Endpoint;
+import locate.v1_0.Endpoints;
+
+public class AAFLocator extends AbsAAFLocator<BasicTrans>  {
+       private static RosettaEnv env;
+       HClient client;
+       private RosettaDF<Endpoints> epsDF;
+
+       public AAFLocator(SecurityInfoC<HttpURLConnection> si, URI locatorURI) throws LocatorException {
+               super(si.access, nameFromLocatorURI(locatorURI), 10000L /* Wait at least 10 seconds between refreshes */);
+               SecuritySetter<HttpURLConnection> ss;
+               try {
+                       ss=AAFConHttp.bestSS(si);
+               } catch (APIException | CadiException e1) {
+                       throw new LocatorException(e1);
+               }
+               synchronized(sr) {
+                       if(env==null) {
+                               env = new RosettaEnv(access.getProperties());
+                       }
+               }
+               
+               int connectTimeout = Integer.parseInt(si.access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
+               try {
+                       String[] path = Split.split('/',locatorURI.getPath());
+                       if(path.length>2 && "locate".equals(path[1])) {
+                               StringBuilder sb = new StringBuilder();
+                               for(int i=3;i<path.length;++i) {
+                                       sb.append('/');
+                                       sb.append(path[i]);
+                               }
+                               setPathInfo(sb.toString());
+                               String host = locatorURI.getHost();
+                               if(aaf_locator_host!=null && (host==null || "AAF_LOCATOR_URL".equals(host))) {
+                                       int slash = aaf_locator_host.lastIndexOf("//");
+                                       host = aaf_locator_host.substring(slash+2);
+                               }
+                               client = new HClient(ss, new URI(
+                                                                                       locatorURI.getScheme(),
+                                                                                       locatorURI.getUserInfo(),
+                                                                                       host,
+                                                                                       locatorURI.getPort(),
+                                                                                       "/locate/"+name + '/' + version,
+                                                                                       null,
+                                                                                       null
+                                                                                       ), connectTimeout);
+                       } else {
+                               client = new HClient(ss, locatorURI, connectTimeout);
+                       }
+                       epsDF = env.newDataFactory(Endpoints.class);
+                       refresh();
+               } catch (APIException | URISyntaxException e) {
+                       throw new LocatorException(e);
+               }
+       }
+
+       @Override
+       public boolean refresh() {
+               try {
+                       client.setMethod("GET");
+                       client.send();
+                       Future<Endpoints> fr = client.futureRead(epsDF, TYPE.JSON);
+                       if(fr.get(client.timeout())) {
+                               List<EP> epl = new LinkedList<EP>();
+                               for(Endpoint endpoint : fr.value.getEndpoint()) {
+                                       epl.add(new EP(endpoint,latitude,longitude));
+                               }
+                               
+                               Collections.sort(epl);
+                               replace(epl);
+                               return true;
+                       } else {
+                               env.error().printf("Error reading location information from %s: %d %s\n",client.getURI().toString(),fr.code(),fr.body());
+                       }
+               } catch (CadiException | URISyntaxException | APIException e) {
+                       env.error().log(e,"Error connecting " + client.getURI() + " for location.");
+               }
+               return false;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator#getURI()
+        */
+       @Override
+       protected URI getURI() {
+               return client.getURI();
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java
new file mode 100644 (file)
index 0000000..41f237d
--- /dev/null
@@ -0,0 +1,243 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.ConnectException;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.Map;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.lur.LocalPermission;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Split;
+
+import aaf.v2_0.Perm;
+import aaf.v2_0.Perms;
+
+/**
+ * Use AAF Service as Permission Service.
+ * 
+ * This Lur goes after AAF Permissions, which are elements of Roles, not the Roles themselves.
+ * 
+ * If you want a simple Role Lur, use AAFRoleLur
+ * 
+ * @author Jonathan
+ *
+ */
+public class AAFLurPerm extends AbsAAFLur<AAFPermission> {
+       /**
+        *  Need to be able to transmutate a Principal into either ATTUID or MechID, which are the only ones accepted at this
+        *  point by AAF.  There is no "domain", aka, no "@att.com" in "ab1234@att.com".  
+        *  
+        *  The only thing that matters here for AAF is that we don't waste calls with IDs that obviously aren't valid.
+        *  Thus, we validate that the ID portion follows the rules before we waste time accessing AAF remotely
+        * @throws APIException 
+        * @throws URISyntaxException 
+        * @throws DME2Exception 
+        */
+       // Package on purpose
+       AAFLurPerm(AAFCon<?> con) throws CadiException, APIException {
+               super(con);
+               attachOAuth2(con);
+       }
+
+       // Package on purpose
+       AAFLurPerm(AAFCon<?> con, AbsUserCache<AAFPermission> auc) throws APIException {
+               super(con,auc);
+               attachOAuth2(con);
+       }
+       
+       private void attachOAuth2(AAFCon<?> con) throws APIException {
+               String oauth2_url;
+               Class<?> tmcls = Config.loadClass(access,"org.osaaf.cadi.oauth.TokenMgr");
+               if(tmcls!=null) {
+                       if((oauth2_url = con.access.getProperty(Config.CADI_OAUTH2_URL,null))!=null) {
+                               try {
+                                       Constructor<?> tmconst = tmcls.getConstructor(AAFCon.class,String.class);
+                                       Object tokMangr = tmconst.newInstance(con,oauth2_url);
+                                       @SuppressWarnings("unchecked")
+                                       Class<Lur> oa2cls = (Class<Lur>)Config.loadClass(access,"org.osaaf.cadi.oauth.OAuth2Lur");
+                                       Constructor<Lur> oa2const = oa2cls.getConstructor(tmcls);
+                                       Lur oa2 = oa2const.newInstance(tokMangr);
+                                       setPreemptiveLur(oa2);
+                               } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                                       throw new APIException(e);
+                               }
+                       } else {
+                               access.log(Level.INIT, "Both cadi-oauth jar and Property",Config.CADI_OAUTH2_URL,"is required to initialize OAuth2");
+                       }
+               }
+       }
+
+       protected User<AAFPermission> loadUser(final Principal principal)  {
+               final String name = principal.getName();
+//             // Note: The rules for AAF is that it only stores permissions for ATTUID and MechIDs, which don't 
+//             // have domains.  We are going to make the Transitive Class (see this.transmutative) to convert
+//             final Principal tp = principal; //transmutate.mutate(principal);
+//             if(tp==null) {
+//                     return null; // if not a valid Transmutated credential, don't bother calling...
+//             }
+//             TODO Create a dynamic way to declare domains supported.
+               final long start = System.nanoTime();
+               final boolean[] success = new boolean[]{false};
+               
+//             new Exception("loadUser").printStackTrace();
+               try {
+                       return aaf.best(new Retryable<User<AAFPermission>>() {
+                               @Override
+                               public User<AAFPermission> code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                                       Future<Perms> fp = client.read("/authz/perms/user/"+name,aaf.permsDF);
+                                       
+                                       // In the meantime, lookup User, create if necessary
+                                       User<AAFPermission> user = getUser(principal);
+                                       Principal p;
+                                       if(user!=null && user.principal == null) {
+                                               p = new Principal() {// Create a holder for lookups
+                                                       private String n = name;
+                                                       public String getName() {
+                                                               return n;
+                                                       }
+                                               };
+                                       } else {
+                                               p = principal;
+                                       }
+                                       
+                                       if(user==null) {
+                                               addUser(user = new User<AAFPermission>(p,aaf.userExpires)); // no password
+                                       }
+                                       
+                                       // OK, done all we can, now get content
+                                       if(fp.get(aaf.timeout)) {
+                                               success[0]=true;
+                                               Map<String, Permission> newMap = user.newMap();
+                                               boolean willLog = aaf.access.willLog(Level.DEBUG);
+                                               for(Perm perm : fp.value.getPerm()) {
+                                                       user.add(newMap,new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction(),perm.getRoles()));
+                                                       if(willLog) {
+                                                               aaf.access.log(Level.DEBUG, name,"has '",perm.getType(),'|',perm.getInstance(),'|',perm.getAction(),'\'');
+                                                       }
+                                               }
+                                               user.setMap(newMap);
+                                       } else {
+                                               int code;
+                                               switch(code=fp.code()) {
+                                                       case 401:
+                                                               aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls");
+                                                               break;
+                                                       case 404:
+                                                               user.setNoPerms();
+                                                               break;
+                                                       default:
+                                                               aaf.access.log(Access.Level.ERROR, code, fp.body());
+                                               }
+                                       }
+
+                                       return user;
+                               }
+                       });
+               } catch (Exception e) {
+                       aaf.access.log(e,"Calling","/authz/perms/user/"+name);
+                       success[0]=false;
+                       return null;
+               } finally {
+                       float time = (System.nanoTime()-start)/1000000f;
+                       aaf.access.log(Level.INFO, success[0]?"Loaded":"Load Failure",name,"from AAF in",time,"ms");
+               }
+       }
+
+       public Resp reload(User<AAFPermission> user) {
+               final String name = user.name;
+               long start = System.nanoTime();
+               boolean success = false;
+               try {
+                       Future<Perms> fp = aaf.client(Config.AAF_DEFAULT_VERSION).read(
+                                       "/authz/perms/user/"+name,
+                                       aaf.permsDF
+                                       );
+                       
+                       // OK, done all we can, now get content
+                       if(fp.get(aaf.timeout)) {
+                               success = true;
+                               Map<String,Permission> newMap = user.newMap(); 
+                               boolean willLog = aaf.access.willLog(Level.DEBUG);
+                               for(Perm perm : fp.value.getPerm()) {
+                                       user.add(newMap, new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction(),perm.getRoles()));
+                                       if(willLog) {
+                                               aaf.access.log(Level.DEBUG, name,"has",perm.getType(),perm.getInstance(),perm.getAction());
+                                       }
+                               }
+                               user.renewPerm();
+                               return Resp.REVALIDATED;
+                       } else {
+                               int code;
+                               switch(code=fp.code()) {
+                                       case 401:
+                                               aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls");
+                                               break;
+                                       default:
+                                               aaf.access.log(Access.Level.ERROR, code, fp.body());
+                               }
+                               return Resp.UNVALIDATED;
+                       }
+               } catch (Exception e) {
+                       aaf.access.log(e,"Calling","/authz/perms/user/"+name);
+                       return Resp.INACCESSIBLE;
+               } finally {
+                       float time = (System.nanoTime()-start)/1000000f;
+                       aaf.access.log(Level.AUDIT, success?"Reloaded":"Reload Failure",name,"from AAF in",time,"ms");
+               }
+       }
+
+       @Override
+       protected boolean isCorrectPermType(Permission pond) {
+               return pond instanceof AAFPermission;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String)
+        */
+       @Override
+       public Permission createPerm(String p) {
+               String[] params = Split.split('|', p);
+               if(params.length==3) {
+                       return new AAFPermission(params[0],params[1],params[2]);
+               } else {
+                       return new LocalPermission(p);
+               }
+       }
+       
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java
new file mode 100644 (file)
index 0000000..42f3ec4
--- /dev/null
@@ -0,0 +1,203 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Connector;
+import org.onap.aaf.cadi.GetCred;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Taf.LifeForm;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon.GetSetter;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp;
+import org.onap.aaf.misc.env.APIException;
+
+public class AAFTaf<CLIENT> extends AbsUserCache<AAFPermission> implements HttpTaf {
+//     private static final String INVALID_AUTH_TOKEN = "Invalid Auth Token";
+//     private static final String AUTHENTICATING_SERVICE_UNAVAILABLE = "Authenticating Service unavailable";
+       private AAFCon<CLIENT> aaf;
+       private boolean warn;
+
+       public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning) {
+               super(con.access,con.cleanInterval,con.highCount, con.usageRefreshTriggerCount);
+               aaf = con;
+               warn = turnOnWarning;
+       }
+
+       public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning, AbsUserCache<AAFPermission> other) {
+               super(other);
+               aaf = (AAFCon<CLIENT>)con;
+               warn = turnOnWarning;
+       }
+       
+       // Note: Needed for Creation of this Object with Generics
+       @SuppressWarnings("unchecked")
+       public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning, AbsUserCache<AAFPermission> other) throws CadiException {
+               this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning,other);
+       }
+
+       // Note: Needed for Creation of this Object with Generics
+       @SuppressWarnings("unchecked")
+       public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning) throws CadiException {
+               this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning);
+       }
+
+
+       public TafResp validate(final LifeForm reading, final HttpServletRequest req, final HttpServletResponse resp) {
+               //TODO Do we allow just anybody to validate?
+
+               // Note: Either Carbon or Silicon based LifeForms ok
+               String authz = req.getHeader("Authorization");
+               if(authz != null && authz.startsWith("Basic ")) {
+                       if(warn&&!req.isSecure())aaf.access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
+                       try {
+                               final CachedBasicPrincipal bp;
+                               if(req.getUserPrincipal() instanceof CachedBasicPrincipal) {
+                                       bp = (CachedBasicPrincipal)req.getUserPrincipal();
+                               } else {
+                                       bp = new CachedBasicPrincipal(this,authz,aaf.getRealm(),aaf.userExpires);
+                               }
+                               // First try Cache
+                               final User<AAFPermission> usr = getUser(bp);
+                               if(usr != null && usr.principal != null) {
+                                       if(usr.principal instanceof GetCred) {
+                                               if(Hash.isEqual(bp.getCred(),((GetCred)usr.principal).getCred())) {
+                                                       return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by cached AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
+                                               }
+                                       }
+                               }
+                               
+                               Miss miss = missed(bp.getName(), bp.getCred());
+                               if(miss!=null && !miss.mayContinue()) {
+                                       return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
+                                                       "User/Pass Retry limit exceeded"), 
+                                                       RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
+                               }
+                               
+                               return aaf.bestForUser(
+                                       new GetSetter() {
+                                               @Override
+                                               public <CL> SecuritySetter<CL> get(AAFCon<CL> con) throws CadiException {
+                                                       return con.basicAuthSS(bp);
+                                               }
+                                       },new Retryable<BasicHttpTafResp>() {
+                                               @Override
+                                               public BasicHttpTafResp code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                                                       Future<String> fp = client.read("/authn/basicAuth", "text/plain");
+                                                       if(fp.get(aaf.timeout)) {
+                                                               if(usr!=null) {
+                                                                       usr.principal = bp;
+                                                               } else {
+                                                                       addUser(new User<AAFPermission>(bp,aaf.userExpires));
+                                                               }
+                                                               return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
+                                                       } else {
+                                                               // Note: AddMiss checks for miss==null, and is part of logic
+                                                               boolean rv= addMiss(bp.getName(),bp.getCred());
+                                                               if(rv) {
+                                                                       return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
+                                                                                       "user/pass combo invalid via AAF from " + req.getRemoteAddr()), 
+                                                                                       RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
+                                                               } else {
+                                                                       return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
+                                                                                       "user/pass combo invalid via AAF from " + req.getRemoteAddr() + " - Retry limit exceeded"), 
+                                                                                       RESP.FAIL,resp,aaf.getRealm(),true);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               );
+                       } catch (IOException e) {
+                               String msg = buildMsg(null,req,"Invalid Auth Token");
+                               aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
+                               return new BasicHttpTafResp(aaf.access,null,msg, RESP.TRY_AUTHENTICATING, resp, aaf.getRealm(),true);
+                       } catch (Exception e) {
+                               String msg = buildMsg(null,req,"Authenticating Service unavailable");
+                               try {
+                                       aaf.invalidate();
+                               } catch (CadiException e1) {
+                                       aaf.access.log(e1, "Error Invalidating Client");
+                               }
+                               aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
+                               return new BasicHttpTafResp(aaf.access,null,msg, RESP.FAIL, resp, aaf.getRealm(),false);
+                       }
+               }
+               return new BasicHttpTafResp(aaf.access,null,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),false);
+       }
+       
+       public String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) {
+               StringBuilder sb = new StringBuilder();
+               for(Object s : msg) {
+                       sb.append(s.toString());
+               }
+               if(pr!=null) {
+                       sb.append(" for ");
+                       sb.append(pr.getName());
+               }
+               sb.append(" from ");
+               sb.append(req.getRemoteAddr());
+               sb.append(':');
+               sb.append(req.getRemotePort());
+               return sb.toString();
+       }
+
+
+       
+       public Resp revalidate(CachedPrincipal prin, Object state) {
+               //  !!!! TEST THIS.. Things may not be revalidated, if not BasicPrincipal
+               if(prin instanceof BasicPrincipal) {
+                       Future<String> fp;
+                       try {
+                               Rcli<CLIENT> userAAF = aaf.client(Config.AAF_DEFAULT_VERSION).forUser(aaf.transferSS((BasicPrincipal)prin));
+                               fp = userAAF.read("/authn/basicAuth", "text/plain");
+                               return fp.get(aaf.timeout)?Resp.REVALIDATED:Resp.UNVALIDATED;
+                       } catch (Exception e) {
+                               aaf.access.log(e, "Cannot Revalidate",prin.getName());
+                               return Resp.INACCESSIBLE;
+                       }
+               }
+               return Resp.NOT_MINE;
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java
new file mode 100644 (file)
index 0000000..074b704
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.TrustChecker;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.principal.TrustPrincipal;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TrustNotTafResp;
+import org.onap.aaf.cadi.taf.TrustTafResp;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.util.Split;
+
+public class AAFTrustChecker implements TrustChecker {
+       private final String tag, id;
+       private final AAFPermission perm;
+       private Lur lur;
+
+       /**
+        * 
+        * Instance will be replaced by Identity
+        * @param lur 
+        *    
+        * @param tag
+        * @param perm
+        */
+       public AAFTrustChecker(final Env env) {
+               tag = env.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN);
+               id = env.getProperty(Config.CADI_ALIAS,env.getProperty(Config.AAF_APPID)); // share between components
+               String str = env.getProperty(Config.CADI_TRUST_PERM);
+               AAFPermission temp=null;
+               if(str!=null) {
+                       String[] sp = Split.splitTrim('|', str);
+                       if(sp.length==3) {
+                               temp = new AAFPermission(sp[0],sp[1],sp[2]);
+                       }
+               }
+               perm=temp;
+       }
+
+       public AAFTrustChecker(final Access access) {
+               tag = access.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN);
+               id = access.getProperty(Config.CADI_ALIAS,access.getProperty(Config.AAF_APPID,null)); // share between components
+               String str = access.getProperty(Config.CADI_TRUST_PERM,null);
+               AAFPermission temp=null;
+               if(str!=null) {
+                       String[] sp = Split.splitTrim('|', str);
+                       if(sp.length==3) {
+                               temp = new AAFPermission(sp[0],sp[1],sp[2]);
+                       }
+               }
+               perm=temp;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.TrustChecker#setLur(org.onap.aaf.cadi.Lur)
+        */
+       @Override
+       public void setLur(Lur lur) {
+               this.lur = lur;
+       }
+
+       @Override
+       public TafResp mayTrust(TafResp tresp, HttpServletRequest req) {
+               String user_info = req.getHeader(tag);
+               if(user_info !=null ) {
+                       String[] info = Split.split(',', user_info);
+                       if(info.length>0) {
+                               String[] flds = Split.splitTrim(':',info[0]);
+                               if(flds.length>3 && "AS".equals(flds[3])) { // is it set for "AS"
+                                       String pn = tresp.getPrincipal().getName();
+                                       if(pn.equals(id)  // We do trust our own App Components: if a trust entry is made with self, always accept
+                                          || lur.fish(tresp.getPrincipal(), perm)) { // Have Perm set by Config.CADI_TRUST_PERM
+                                               return new TrustTafResp(tresp,
+                                                               new TrustPrincipal(tresp.getPrincipal(), flds[0]),
+                                                               "  " + flds[0] + " validated using " + flds[2] + " by " + flds[1] + ','
+                                                       );
+                                       } else if(pn.equals(flds[0])) { // Ignore if same identity 
+                                               return tresp;
+                                       } else {
+                                               return new TrustNotTafResp(tresp, tresp.getPrincipal().getName() + " requested trust as "
+                                                               + flds[0] + ", but does not have Authorization");
+                                       }
+                               }
+                       }
+               }
+               return tresp;
+       }
+
+}
\ No newline at end of file
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java
new file mode 100644 (file)
index 0000000..19beef6
--- /dev/null
@@ -0,0 +1,496 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+import org.onap.aaf.cadi.routing.GreatCircle;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.util.Split;
+
+import locate.v1_0.Endpoint;
+
+public abstract class AbsAAFLocator<TRANS extends Trans> implements Locator<URI> {
+       protected static final SecureRandom sr = new SecureRandom();
+       private static LocatorCreator locatorCreator;
+       protected final Access access;
+
+       protected final double latitude;
+       protected final double longitude;
+       protected List<EP> epList;
+       protected final String name, version;
+       private String pathInfo = null;
+       private String query = null;
+       private String fragment = null;
+       private boolean additional = false;
+       protected String myhostname;
+       protected int myport;
+       protected final String aaf_locator_host;
+       private long earliest;
+       private final long refreshWait;
+
+
+       public AbsAAFLocator(Access access, String name, final long refreshMin) throws LocatorException {
+               aaf_locator_host = access.getProperty(Config.AAF_LOCATE_URL, null);
+
+               epList = new LinkedList<EP>();
+               refreshWait = refreshMin;
+
+               this.access = access;
+               String lat = access.getProperty(Config.CADI_LATITUDE,null);
+               String lng = access.getProperty(Config.CADI_LONGITUDE,null);
+               if(lat==null || lng==null) {
+                       throw new LocatorException(Config.CADI_LATITUDE + " and " + Config.CADI_LONGITUDE + " properties are required.");
+               } else {
+                       latitude = Double.parseDouble(lat);
+                       longitude = Double.parseDouble(lng);
+               }
+               if(name.startsWith("http")) { // simple URL
+                       this.name = name;
+                       this.version = Config.AAF_DEFAULT_VERSION;
+               } else {
+                       String[] split = Split.split(':', name);
+                       
+                       switch(split.length) {
+                               case 1:
+                                       this.name = split[0];
+                                       this.version = Config.AAF_DEFAULT_VERSION;
+                                       break;
+                               case 0:
+                                       this.name = name;
+                                       this.version = Config.AAF_DEFAULT_VERSION;
+                                       break;
+                               default:
+                                       this.version = split[1];
+                                       this.name = split[0];
+                                       
+                       }
+               }
+               
+       }
+
+       /**
+        * This is the way to setup specialized AAFLocators ahead of time.
+        * @param preload
+        */
+       public static void setCreator(LocatorCreator lc) {
+               locatorCreator = lc; 
+       }
+       
+       public static Locator<URI> create(String key) throws LocatorException {
+               String name = null;
+               String version = Config.AAF_DEFAULT_VERSION;
+               String pathInfo = null;
+               int prev = key.indexOf("/locate");
+               if(prev>0) {
+                       prev = key.indexOf('/',prev+6);
+                       if(prev>0) {
+                               int next = key.indexOf('/',++prev);
+                               if(next>0) {
+                                       name = key.substring(prev, next);
+                                       pathInfo=key.substring(next);
+                               } else {
+                                       name = key.substring(prev);
+                               }
+                               String[] split = Split.split(':', name);
+                               switch(split.length) {
+                                       case 3:
+                                       case 2:
+                                               version = split[1];
+                                               name = split[0];
+                                               break;
+                               }
+                       }
+               }
+
+               if(key.startsWith("http")) {
+                       if(name!=null) {
+                               if(locatorCreator != null) {
+                                       if(name!=null) {
+                                               AbsAAFLocator<?> aal = locatorCreator.create(name, version);
+                                               if(pathInfo!=null) {
+                                                       aal.setPathInfo(pathInfo);
+                                               }
+                                               return aal;
+                                       }
+                               }
+                       } else {
+                               return new PropertyLocator(key);
+                       }
+               }
+               return null;
+       }
+       
+       public static Locator<URI> create(final String name, final String version) throws LocatorException {
+               return locatorCreator.create(name, version);
+       }
+
+       public interface LocatorCreator {
+               public AbsAAFLocator<?> create(String key, String version) throws LocatorException;
+               public void setSelf(String hostname, int port);
+       }
+
+       protected static String nameFromLocatorURI(URI locatorURI) {
+               String[] path = Split.split('/', locatorURI.getPath());
+               if(path.length>2 && "locate".equals(path[1])) {
+                       return path[2];
+               } else {
+                       return locatorURI.toString();
+               }
+       }
+       
+       /**
+        * Setting "self" excludes this service from the list.  Critical for contacting peers. 
+        */
+       public void setSelf(final String hostname, final int port) {
+               myhostname=hostname;
+               myport=port;
+       }
+
+
+       public static void setCreatorSelf(final String hostname, final int port) {
+               if(locatorCreator!=null) {
+                       locatorCreator.setSelf(hostname,port);
+               }
+       }
+
+       protected final synchronized void replace(List<EP> list) {
+               epList = list;
+       }
+       
+       /**
+        * Call _refresh as needed during calls, but actual refresh will not occur if there
+        * are existing entities or if it has been called in the last 10 (settable) seconds.  
+        * Timed Refreshes happen by Scheduled Thread
+        */
+       private final boolean _refresh() {
+               boolean rv = false;
+               long now=System.currentTimeMillis();
+               if(noEntries()) {
+                       if(earliest<now) {
+                               synchronized(epList) {
+                                       rv = refresh();
+                                       earliest = now + refreshWait; // call only up to 10 seconds.
+                               }
+                       } else {
+                               access.log(Level.ERROR, "Must wait at least " + refreshWait/1000 + " seconds for Locator Refresh");
+                       }
+               }
+               return rv;
+       }
+
+       private boolean noEntries() {
+               return epList.size()<=0;
+       }
+
+       @Override
+       public URI get(Item item) throws LocatorException {
+               if(item==null) {
+                       return null;
+               } else if(item instanceof AAFLItem) {
+                       return getURI(((AAFLItem)item).uri);
+               } else {
+                       throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
+               }
+       }
+
+       @Override
+       public boolean hasItems() {
+               boolean isEmpty = epList.isEmpty();
+               if(!isEmpty) {
+                       for(Iterator<EP> iter = epList.iterator(); iter.hasNext(); ) {
+                               EP ep = iter.next();
+                               if(ep.valid) {
+                                       return true;
+                               }
+                       }
+                       isEmpty = true;
+               }
+               if(_refresh()) { // is refreshed... check again
+                       isEmpty = epList.isEmpty();
+               }
+               return !isEmpty;
+       }
+
+       @Override
+       public void invalidate(Item item) throws LocatorException {
+               if(item!=null) {
+                       if(item instanceof AAFLItem) {
+                               AAFLItem ali =(AAFLItem)item; 
+                               EP ep = ali.ep;
+                               synchronized(epList) {
+                                       epList.remove(ep);
+                               }
+                               ep.invalid();
+                               ali.iter = getIterator(); // for next guy... fresh iterator
+                       } else {
+                               throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
+                       }
+               }
+       }
+
+       @Override
+       public Item best() throws LocatorException {
+               if(!hasItems()) {
+                       throw new LocatorException("No Entries found" + (pathInfo==null?"":(" for " + pathInfo)));
+               }
+               List<EP> lep = new ArrayList<EP>();
+               EP first = null;
+               // Note: Deque is sorted on the way by closest distance
+               Iterator<EP> iter = getIterator();
+               EP ep;
+               while(iter.hasNext()) {
+                       ep = iter.next();
+                       if(ep.valid) {
+                               if(first==null) {
+                                       first = ep;
+                                       lep.add(first);
+                               } else {
+                                       if(Math.abs(ep.distance-first.distance)<.1) { // allow for nearby/precision issues.
+                                               lep.add(ep);
+                                       } else {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               switch(lep.size()) {
+                       case 0:
+                               return null;
+                       case 1:
+                               return new AAFLItem(iter,first);
+                       default:
+                               int i = Math.abs(sr.nextInt())%lep.size();
+                               if(i<0) {
+                                       return null;
+                               } else {
+                                       return new AAFLItem(iter,lep.get(i));
+                               }
+                       
+               }
+       }
+
+       private Iterator<EP> getIterator() {
+               Object[] epa = epList.toArray();
+               if(epa.length==0) {
+                       _refresh();
+                       epa = epList.toArray();
+               }
+               return new EPIterator(epa, epList);
+       }
+
+       public class EPIterator implements Iterator<EP> {
+               private final Object[] epa;
+               private final List<EP> epList;
+               private int idx;
+               
+               public EPIterator(Object[] epa, List<EP> epList) {
+                       this.epa = epa;
+                       this.epList = epList;
+                       idx = epa.length>0?0:-1;
+               }
+
+               @Override
+               public boolean hasNext() {
+                       if(idx<0) {
+                               return false;
+                       } else {
+                               Object obj;
+                               while(idx<epa.length) {
+                                       if((obj=epa[idx])==null || !((EP)obj).valid) {
+                                               ++idx;
+                                               continue;
+                                       }
+                                       break;
+                               }
+                               return idx<epa.length;
+                       }
+               }
+
+               @Override
+               public EP next() {
+                       return (EP)epa[idx++];
+               }
+
+               @Override
+               public void remove() {
+                       if(idx>=0 && idx<epa.length) {
+                               synchronized(epList) {
+                                       epList.remove(epa[idx]);
+                               }
+                       }
+               }
+       }
+       
+       @Override
+       public Item first()  {
+               Iterator<EP> iter = getIterator();
+               EP ep = AAFLItem.next(iter);
+               if(ep==null) {
+                       return null;
+               }
+               return new AAFLItem(iter,ep);
+       }
+
+       @Override
+       public Item next(Item prev) throws LocatorException {
+               if(prev==null) {
+                       StringBuilder sb = new StringBuilder("Locator Item passed in next(item) is null.");
+                       int lines = 0;
+                       for(StackTraceElement st : Thread.currentThread().getStackTrace()) {
+                               sb.append("\n\t");
+                               sb.append(st.toString());
+                               if(++lines > 5) {
+                                       sb.append("\n\t...");
+                                       break;
+                               }
+                       }
+                       access.log(Level.ERROR, sb);
+               } else {
+                       if(prev instanceof AAFLItem) {
+                               AAFLItem ali = (AAFLItem)prev;
+                               EP ep = AAFLItem.next(ali.iter);
+                               if(ep!=null) {
+                                       return new AAFLItem(ali.iter,ep);
+                               }
+                       } else {
+                               throw new LocatorException(prev.getClass().getName() + " does not belong to AAFLocator");
+                       }
+               }
+               return null;
+       }
+
+       protected static class AAFLItem implements Item {
+                       private Iterator<EP> iter;
+                       private URI uri;
+                       private EP ep;
+       
+                       public AAFLItem(Iterator<EP> iter, EP ep) {
+                               this.iter = iter;
+                               this.ep = ep;
+                               uri = ep.uri;
+                       }
+                       
+                       private static EP next(Iterator<EP> iter) {
+                               EP ep=null;
+                               while(iter.hasNext() && (ep==null || !ep.valid)) {
+                                       ep = iter.next();
+                               }
+                               return ep;
+                       }
+                       
+                       public String toString() {
+                               return ep==null?"Locator Item Invalid":ep.toString();
+                       }
+               }
+
+       protected static class EP implements Comparable<EP> {
+               public URI uri;
+               public final double distance;
+               private boolean valid;
+               
+               public EP(final Endpoint ep, double latitude, double longitude) throws URISyntaxException {
+                       uri = new URI(ep.getProtocol(),null,ep.getHostname(),ep.getPort(),null,null,null);
+                       distance = GreatCircle.calc(latitude, longitude, ep.getLatitude(), ep.getLongitude());
+                       valid = true;
+               }
+
+               public void invalid() {
+                       valid = false;
+               }
+
+               @Override
+               public int compareTo(EP o) {
+                       if(distance<o.distance) {
+                               return -1;
+                       } else if(distance==o.distance) {
+                               return 0;
+                       } else {
+                               return 1;
+                       }
+               }
+               
+               @Override
+               public String toString() {
+                       return distance + ": " + uri + (valid?" valid":" invalidate");
+               }
+       }
+       
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Locator#destroy()
+        */
+       @Override
+       public void destroy() {
+               // Nothing to do
+       }
+       
+       @Override
+       public String toString() {
+               return "AAFLocator for " + name + " on " + getURI();
+       }
+
+       public AbsAAFLocator<TRANS> setPathInfo(String pathInfo) {
+               this.pathInfo = pathInfo;
+               additional=true;
+               return this;
+       }
+
+       public AbsAAFLocator<TRANS> setQuery(String query) {
+               this.query = query;
+               additional=true;
+               return this;
+       }
+
+       public AbsAAFLocator<TRANS>  setFragment(String fragment) {
+               this.fragment = fragment;
+               additional=true;
+               return this;
+       }
+
+       // Core URI, for reporting purposes
+       protected abstract URI getURI();
+
+       protected URI getURI(URI rv) throws LocatorException {
+               if(additional) {
+                       try {
+                               return new URI(rv.getScheme(),rv.getUserInfo(),rv.getHost(),rv.getPort(),pathInfo,query,fragment);
+                       } catch (URISyntaxException e) {
+                               throw new LocatorException("Error coping URL");
+                       }
+               }
+               return rv;
+       }
+
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java
new file mode 100644 (file)
index 0000000..083537a
--- /dev/null
@@ -0,0 +1,289 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.v2_0;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachingLur;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Split;
+
+public abstract class AbsAAFLur<PERM extends Permission> extends AbsUserCache<PERM> implements CachingLur<PERM> {
+       protected static final byte[] BLANK_PASSWORD = new byte[0];
+       private String[] debug = null;
+       public AAFCon<?> aaf;
+       public Lur preemptiveLur=null; // Initial Use is for OAuth2, preemptive Lur
+       private String[] supports;
+
+       public AbsAAFLur(AAFCon<?> con) throws APIException {
+               super(con.access, con.cleanInterval, con.highCount, con.usageRefreshTriggerCount);
+               aaf = con;
+               setLur(this);
+               supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*");
+       }
+
+       public AbsAAFLur(AAFCon<?> con, AbsUserCache<PERM> auc) throws APIException {
+               super(auc);
+               aaf = con;
+               setLur(this);
+               supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*");
+       }
+
+       @Override
+       public void setDebug(String ids) {
+               this.debug = ids==null?null:Split.split(',', ids);
+       }
+       
+       public void setPreemptiveLur(Lur preemptive) {
+               this.preemptiveLur = preemptive;
+       }
+       
+       protected abstract User<PERM> loadUser(Principal bait);
+
+       @Override
+       public final boolean handles(Principal principal) {
+               if(preemptiveLur!=null) {
+                       if(preemptiveLur.handles(principal)) {
+                               return true;
+                       }
+               }
+               String userName=principal.getName();
+               if(userName!=null) {
+                       for(String s : supports) {
+                               if(userName.endsWith(s))
+                                       return true;
+                       }
+               }
+               return false;
+       }
+
+       
+       protected abstract boolean isCorrectPermType(Permission pond);
+       
+       // This is where you build AAF CLient Code.  Answer the question "Is principal "bait" in the "pond"
+       public boolean fish(Principal bait, Permission pond) {
+               if(preemptiveLur!=null && preemptiveLur.handles(bait)) {
+                       return preemptiveLur.fish(bait, pond);
+               } else {
+                       if(pond==null) {
+                               return false;
+                       }
+                       if(isDebug(bait)) {
+                               boolean rv = false;
+                               StringBuilder sb = new StringBuilder("Log for ");
+                               sb.append(bait);
+                               if(handles(bait)) {
+                                       User<PERM> user = getUser(bait);
+                                       if(user==null) {
+                                               sb.append("\n\tUser is not in Cache");
+                                       } else {
+                                               if(user.noPerms()) {
+                                                       sb.append("\n\tUser has no Perms");
+                                               }
+                                               if(user.permExpired()) {
+                                                       sb.append("\n\tUser's perm expired [");
+                                                       sb.append(new Date(user.permExpires()));
+                                                       sb.append(']');
+                                               } else {
+                                                       sb.append("\n\tUser's perm expires [");
+                                                       sb.append(new Date(user.permExpires()));
+                                                       sb.append(']');
+                                               }
+                                       }
+                                       if(user==null || user.permsUnloaded() || user.permExpired()) {
+                                               user = loadUser(bait);
+                                               sb.append("\n\tloadUser called");
+                                       }
+                                       if(user==null) {
+                                               sb.append("\n\tUser was not Loaded");
+                                       } else if(user.contains(pond)) {
+                                               sb.append("\n\tUser contains ");
+                                               sb.append(pond.getKey());
+                                               rv = true;
+                                       } else {
+                                               sb.append("\n\tUser does not contain ");
+                                               sb.append(pond.getKey());
+                                               List<Permission> perms = new ArrayList<Permission>();
+                                               user.copyPermsTo(perms);
+                                               for(Permission p : perms) {
+                                                       sb.append("\n\t\t");
+                                                       sb.append(p.getKey());
+                                               }
+                                       }
+                               } else {
+                                       sb.append("AAF Lur does not support [");
+                                       sb.append(bait);
+                                       sb.append("]");
+                               }
+                               aaf.access.log(Level.INFO, sb);
+                               return rv;
+                       } else {
+                               if(handles(bait)) {
+                                       User<PERM> user = getUser(bait);
+                                       if(user==null || user.permsUnloaded() || user.permExpired()) {
+                                               user = loadUser(bait);
+                                       }
+                                       return user==null?false:user.contains(pond);
+                               }
+                               return false;
+                       }
+               }
+       }
+
+       public void fishAll(Principal bait, List<Permission> perms) {
+               if(preemptiveLur!=null && preemptiveLur.handles(bait)) {
+                       preemptiveLur.fishAll(bait, perms);
+               } else {
+                       if(isDebug(bait)) {
+                               StringBuilder sb = new StringBuilder("Log for ");
+                               sb.append(bait);
+                               if(handles(bait)) {
+                                       User<PERM> user = getUser(bait);
+                                       if(user==null) {
+                                               sb.append("\n\tUser is not in Cache");
+                                       } else {
+                                               if(user.noPerms()) {
+                                                       sb.append("\n\tUser has no Perms");
+                                               }
+                                               if(user.permExpired()) {
+                                                       sb.append("\n\tUser's perm expired [");
+                                                       sb.append(new Date(user.permExpires()));
+                                                       sb.append(']');
+                                               } else {
+                                                       sb.append("\n\tUser's perm expires [");
+                                                       sb.append(new Date(user.permExpires()));
+                                                       sb.append(']');
+                                               }
+                                       }
+                                       if(user==null || user.permsUnloaded() || user.permExpired()) {
+                                               user = loadUser(bait);
+                                               sb.append("\n\tloadUser called");
+                                       }
+                                       if(user==null) {
+                                               sb.append("\n\tUser was not Loaded");
+                                       } else {
+                                               sb.append("\n\tCopying Perms ");
+                                               user.copyPermsTo(perms);
+                                               for(Permission p : perms) {
+                                                       sb.append("\n\t\t");
+                                                       sb.append(p.getKey());
+                                               }
+                                       }
+                               } else {
+                                       sb.append("AAF Lur does not support [");
+                                       sb.append(bait);
+                                       sb.append("]");
+                               }
+                               aaf.access.log(Level.INFO, sb);
+                       } else {
+                               if(handles(bait)) {
+                                       User<PERM> user = getUser(bait);
+                                       if(user==null || user.permsUnloaded() || user.permExpired()) {
+                                               user = loadUser(bait);
+                                       }
+                                       if(user!=null) {
+                                               user.copyPermsTo(perms);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       @Override
+       public void remove(String user) {
+               super.remove(user);
+       }
+
+       private boolean isDebug(Principal p) {
+               if(debug!=null) {
+                       if(debug.length==1 && "all".equals(debug[0])) {
+                               return true;
+                       }
+                       String name = p.getName();
+                       for(String s : debug) {
+                               if(s.equals(name)) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+       /**
+        * This special case minimizes loops, avoids multiple Set hits, and calls all the appropriate Actions found.
+        * 
+        * @param bait
+        * @param obj
+        * @param type
+        * @param instance
+        * @param actions
+        */
+       public<A> void fishOneOf(Principal princ, A obj, String type, String instance, List<Action<A>> actions) {
+               User<PERM> user = getUser(princ);
+               if(user==null || user.permsUnloaded() || user.permExpired()) {
+                       user = loadUser(princ);
+               }
+               if(user!=null) {
+                       ReuseAAFPermission perm = new ReuseAAFPermission(type,instance);
+                       for(Action<A> action : actions) {
+                               perm.setAction(action.getName());
+                               if(user.contains(perm)) {
+                                       if(action.exec(obj))return;
+                               }
+                       }
+               }
+       }
+       
+       public static interface Action<A> {
+               public String getName();
+               /**
+                *  Return false to continue, True to end now
+                * @return
+                */
+               public boolean exec(A a);
+       }
+       
+       private class ReuseAAFPermission extends AAFPermission {
+               public ReuseAAFPermission(String type, String instance) {
+                       super(type,instance,null,null);
+               }
+
+               public void setAction(String s) {
+                       action = s;
+               }
+               
+               /**
+                * This function understands that AAF Keys are hierarchical, :A:B:C, 
+                *  Cassandra follows a similar method, so we'll short circuit and do it more efficiently when there isn't a first hit
+                * @return
+                */
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java
new file mode 100644 (file)
index 0000000..3669d04
--- /dev/null
@@ -0,0 +1,286 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.util.Chmod;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.util.Chrono;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+public abstract class ArtifactDir implements PlaceArtifact {
+
+       protected static final String C_R = "\n";
+       protected File dir;
+       private List<String> encodeds = new ArrayList<String>();
+       
+       private Symm symm;
+       // This checks for multiple passes of Dir on the same objects.  Run clear after done.
+       protected static Map<String,Object> processed = new HashMap<String,Object>();
+
+
+       /**
+        * Note:  Derived Classes should ALWAYS call "super.place(cert,arti)" first, and 
+        * then "placeProperties(arti)" just after they implement
+        */
+       @Override
+       public final boolean place(Trans trans, CertInfo certInfo, Artifact arti, String machine) throws CadiException {
+               validate(arti);
+               
+               try {
+                       // Obtain/setup directory as required
+                       dir = new File(arti.getDir());
+                       if(processed.get("dir")==null) {
+                               if(!dir.exists()) {
+                                       Chmod.to755.chmod(dir);
+                                       if(!dir.mkdirs()) {
+                                               throw new CadiException("Could not create " + dir);
+                                       }
+                               }
+                               
+                               // Also place cm_url and Host Name
+                               addProperty(Config.CM_URL,trans.getProperty(Config.CM_URL));
+                               addProperty(Config.HOSTNAME,machine);
+                               addProperty(Config.AAF_ENV,certInfo.getEnv());
+                               // Obtain Issuers
+                               boolean first = true;
+                               StringBuilder issuers = new StringBuilder();
+                               for(String dn : certInfo.getCaIssuerDNs()) {
+                                       if(first) {
+                                               first=false;
+                                       } else {
+                                               issuers.append(':');
+                                       }
+                                       issuers.append(dn);
+                               }
+                               addProperty(Config.CADI_X509_ISSUERS,issuers.toString());
+                       }
+                       symm = (Symm)processed.get("symm");
+                       if(symm==null) {
+                               // CADI Key Gen
+                               File f = new File(dir,arti.getNs() + ".keyfile");
+                               if(!f.exists()) {
+                                       write(f,Chmod.to400,Symm.keygen());
+                               }
+                               symm = Symm.obtain(f); 
+
+                               addEncProperty("ChallengePassword", certInfo.getChallenge());
+                               
+                               processed.put("symm",symm);
+                       }
+
+                       _place(trans, certInfo,arti);
+                       
+                       placeProperties(arti);
+                       
+                       processed.put("dir",dir);
+
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+               return true;
+       }
+
+       /**
+        * Derived Classes implement this instead, so Dir can process first, and write any Properties last
+        * @param cert
+        * @param arti
+        * @return
+        * @throws CadiException
+        */
+       protected abstract boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException;
+
+       protected void addProperty(String tag, String value) throws IOException {
+               StringBuilder sb = new StringBuilder();
+               sb.append(tag);
+               sb.append('=');
+               sb.append(value);
+               encodeds.add(sb.toString());
+       }
+
+       protected void addEncProperty(String tag, String value) throws IOException {
+               StringBuilder sb = new StringBuilder();
+               sb.append(tag);
+               sb.append('=');
+               sb.append("enc:");
+               sb.append(symm.enpass(value));
+               encodeds.add(sb.toString());
+       }
+
+       protected void write(File f, Chmod c, String ... data) throws IOException {
+               f.setWritable(true,true);
+               
+               FileOutputStream fos = new FileOutputStream(f);
+               PrintStream ps = new PrintStream(fos);
+               try {
+                       for(String s : data) {
+                               ps.print(s);
+                       }
+               } finally {
+                       ps.close();
+                       c.chmod(f);
+               }
+       }
+
+       protected void write(File f, Chmod c, byte[] bytes) throws IOException {
+               f.setWritable(true,true);
+               
+               FileOutputStream fos = new FileOutputStream(f);
+               try {
+                       fos.write(bytes);
+               } finally {
+                       fos.close();
+                       c.chmod(f);
+               }
+       }
+       
+       protected void write(File f, Chmod c, KeyStore ks, char[] pass ) throws IOException, CadiException {
+               f.setWritable(true,true);
+               
+               FileOutputStream fos = new FileOutputStream(f);
+               try {
+                       ks.store(fos, pass);
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               } finally {
+                       fos.close();
+                       c.chmod(f);
+               }
+       }
+
+
+       private void validate(Artifact a) throws CadiException {
+               StringBuilder sb = new StringBuilder();
+               if(a.getDir()==null) {
+                       sb.append("File Artifacts require a path");
+               }
+
+               if(a.getNs()==null) {
+                       if(sb.length()>0) {
+                               sb.append('\n');
+                       }
+                       sb.append("File Artifacts require an AAF Namespace");
+               }
+               
+               if(sb.length()>0) {
+                       throw new CadiException(sb.toString());
+               }
+       }
+
+       private boolean placeProperties(Artifact arti) throws CadiException {
+               if(encodeds.size()==0) {
+                       return true;
+               }
+               boolean first=processed.get("dir")==null;
+               try {
+                       File f = new File(dir,arti.getNs()+".props");
+                       if(f.exists()) {
+                               if(first) {
+                                       f.delete();
+                               } else {
+                                       f.setWritable(true);
+                               }
+                       }
+                       // Append if not first
+                       PrintWriter pw = new PrintWriter(new FileWriter(f,!first));
+                       
+                       // Write a Header
+                       if(first) {
+                               for(int i=0;i<60;++i) {
+                                       pw.print('#');
+                               }
+                               pw.println();
+                               pw.println("# Properties Generated by AT&T Certificate Manager");
+                               pw.print("#   by ");
+                               pw.println(System.getProperty("user.name"));
+                               pw.print("#   on ");
+                               pw.println(Chrono.dateStamp());
+                               pw.println("# @copyright 2016, AT&T");
+                               for(int i=0;i<60;++i) {
+                                       pw.print('#');
+                               }
+                               pw.println();
+                               for(String prop : encodeds) {
+                                       if(    prop.startsWith("cm_") 
+                                               || prop.startsWith(Config.HOSTNAME)
+                                               || prop.startsWith(Config.AAF_ENV)) {
+                                               pw.println(prop);
+                                       }
+                               }
+                       }
+                       
+                       try {
+                               for(String prop : encodeds) {
+                                       if(prop.startsWith("cadi")) {
+                                               pw.println(prop);
+                                       }
+                               }
+                       } finally {
+                               pw.close();
+                       }
+                       Chmod.to644.chmod(f);
+                       
+                       if(first) {
+                               // Challenge
+                               f = new File(dir,arti.getNs()+".chal");
+                               if(f.exists()) {
+                                       f.delete();
+                               }
+                               pw = new PrintWriter(new FileWriter(f));
+                               try {
+                                       for(String prop : encodeds) {
+                                               if(prop.startsWith("Challenge")) {
+                                                       pw.println(prop);
+                                               }
+                                       }
+                               } finally {
+                                       pw.close();
+                               }
+                               Chmod.to400.chmod(f);
+                       }
+               } catch(Exception e) {
+                       throw new CadiException(e);
+               }
+               return true;
+       }
+       
+       public static void clear() {
+               processed.clear();
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java
new file mode 100644 (file)
index 0000000..5c525ff
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+public class CertException extends Exception {
+
+       /**
+        * 
+        */
+       private static final long serialVersionUID = 1373028409048516401L;
+
+       public CertException() {
+       }
+
+       public CertException(String message) {
+               super(message);
+       }
+
+       public CertException(Throwable cause) {
+               super(cause);
+       }
+
+       public CertException(String message, Throwable cause) {
+               super(message, cause);
+       }
+}
\ No newline at end of file
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java
new file mode 100644 (file)
index 0000000..14efd32
--- /dev/null
@@ -0,0 +1,722 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.aaf.client.ErrMessage;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.http.HBasicAuthSS;
+import org.onap.aaf.cadi.sso.AAFSSO;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.misc.env.util.Split;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import java.util.Properties;
+
+import certman.v1_0.Artifacts;
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+import certman.v1_0.CertificateRequest;
+
+public class CmAgent {
+       private static final String PRINT = "print";
+       private static final String FILE = "file";
+       private static final String PKCS12 = "pkcs12";
+       private static final String JKS = "jks";
+       private static final String SCRIPT="script";
+       
+       private static final String CM_VER = "1.0";
+       public static final int PASS_SIZE = 24;
+       private static int TIMEOUT;
+       
+       private static RosettaDF<CertificateRequest> reqDF;
+       private static RosettaDF<CertInfo> certDF;
+       private static RosettaDF<Artifacts> artifactsDF;
+       private static ErrMessage errMsg;
+       private static Map<String,PlaceArtifact> placeArtifact;
+       private static RosettaEnv env;
+
+       public static void main(String[] args) {
+               int exitCode = 0;
+               try {
+                       AAFSSO aafsso = new AAFSSO(args);
+                       if(aafsso.loginOnly()) {
+                               aafsso.setLogDefault();
+                               aafsso.writeFiles();
+                               System.out.println("AAF SSO information created in ~/.aaf");
+                       } else {
+                               PropAccess access = aafsso.access();
+                               env = new RosettaEnv(access.getProperties());
+                               Deque<String> cmds = new ArrayDeque<String>();
+                               for(String p : args) {
+                                       if(p.indexOf('=')<0) {
+                                               cmds.add(p);
+                                       }
+                               }
+                               
+                               if(cmds.size()==0) {
+                                       aafsso.setLogDefault();
+                                       System.out.println("Usage: java -jar <cadi-aaf-*-full.jar> cmd [<tag=value>]*");
+                                       System.out.println("   create   <mechID> [<machine>]");
+                                       System.out.println("   read     <mechID> [<machine>]");
+                                       System.out.println("   update   <mechID> [<machine>]");
+                                       System.out.println("   delete   <mechID> [<machine>]");
+                                       System.out.println("   copy     <mechID> <machine> <newmachine>[,<newmachine>]*");
+                                       System.out.println("   place    <mechID> [<machine>]");
+                                       System.out.println("   showpass <mechID> [<machine>]");
+                                       System.out.println("   check    <mechID> [<machine>]");
+                                       System.out.println("   genkeypair");
+                                       System.exit(1);
+                               }
+                               
+                               TIMEOUT = Integer.parseInt(env.getProperty(Config.AAF_CONN_TIMEOUT, "5000"));
+                       
+                               reqDF = env.newDataFactory(CertificateRequest.class);
+                               artifactsDF = env.newDataFactory(Artifacts.class);
+                               certDF = env.newDataFactory(CertInfo.class);
+                               errMsg = new ErrMessage(env);
+       
+                               placeArtifact = new HashMap<String,PlaceArtifact>();
+                               placeArtifact.put(JKS, new PlaceArtifactInKeystore(JKS));
+                               placeArtifact.put(PKCS12, new PlaceArtifactInKeystore(PKCS12));
+                               placeArtifact.put(FILE, new PlaceArtifactInFiles());
+                               placeArtifact.put(PRINT, new PlaceArtifactOnStream(System.out));
+                               placeArtifact.put(SCRIPT, new PlaceArtifactScripts());
+                               
+                               Trans trans = env.newTrans();
+                               String token;
+                               if((token=access.getProperty("oauth_token"))!=null) {
+                                       trans.setProperty("oauth_token", token);
+                               }
+                               try {
+                                       // show Std out again
+                                       aafsso.setLogDefault();
+                                       aafsso.setStdErrDefault();
+                                       
+                                       // if CM_URL can be obtained, add to sso.props, if written
+                                       String cm_url = getProperty(access,env,false, Config.CM_URL,Config.CM_URL+": ");
+                                       if(cm_url!=null) {
+                                               aafsso.addProp(Config.CM_URL, cm_url);
+                                       }
+                                       aafsso.writeFiles();
+
+                                       AAFCon<?> aafcon = new AAFConHttp(access,Config.CM_URL);
+
+                                       String cmd = cmds.removeFirst();
+                                       if("place".equals(cmd)) {
+                                               placeCerts(trans,aafcon,cmds);
+                                       } else if("create".equals(cmd)) {
+                                               createArtifact(trans, aafcon,cmds);
+                                       } else if("read".equals(cmd)) {
+                                               readArtifact(trans, aafcon, cmds);
+                                       } else if("copy".equals(cmd)) {
+                                               copyArtifact(trans, aafcon, cmds);
+                                       } else if("update".equals(cmd)) {
+                                               updateArtifact(trans, aafcon, cmds);
+                                       } else if("delete".equals(cmd)) {
+                                               deleteArtifact(trans, aafcon, cmds);
+                                       } else if("showpass".equals(cmd)) {
+                                               showPass(trans,aafcon,cmds);
+                                       } else if("check".equals(cmd)) {
+                                               try {
+                                                       exitCode = check(trans,aafcon,cmds);
+                                               } catch (Exception e) {
+                                                       exitCode = 1;
+                                                       throw e;
+                                               }
+                                       } else {
+                                               AAFSSO.cons.printf("Unknown command \"%s\"\n", cmd);
+                                       }
+                               } finally {
+                                       StringBuilder sb = new StringBuilder();
+                       trans.auditTrail(4, sb, Trans.REMOTE);
+                       if(sb.length()>0) {
+                               trans.info().log("Trans Info\n",sb);
+                       }
+                               }
+                               aafsso.close();
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+               if(exitCode!=0) {
+                       System.exit(exitCode);
+               }
+       }
+
+       private static String getProperty(PropAccess pa, Env env, boolean secure, String tag, String prompt, Object ... def) {
+               String value;
+               if((value=pa.getProperty(tag))==null) {
+                       if(secure) {
+                               value = new String(AAFSSO.cons.readPassword(prompt, def));
+                       } else {
+                               value = AAFSSO.cons.readLine(prompt,def).trim();
+                       }
+                       if(value!=null) {
+                               if(value.length()>0) {
+                                       pa.setProperty(tag,value);
+                                       env.setProperty(tag,value);
+                               } else if(def.length==1) {
+                                       value=def[0].toString();
+                                       pa.setProperty(tag,value);
+                                       env.setProperty(tag,value);
+                               }
+                       }
+               }
+               return value;
+       }
+
+       private static String mechID(Deque<String> cmds) {
+               if(cmds.size()<1) {
+                       String alias = env.getProperty(Config.CADI_ALIAS);
+                       return alias!=null?alias:AAFSSO.cons.readLine("MechID: ");
+               }
+               return cmds.removeFirst();      
+       }
+
+       private static String machine(Deque<String> cmds) throws UnknownHostException {
+               if(cmds.size()>0) {
+                       return cmds.removeFirst();
+               } else {
+                       String mach = env.getProperty(Config.HOSTNAME);
+                       return mach!=null?mach:InetAddress.getLocalHost().getHostName();
+               }
+       }
+
+       private static String[] machines(Deque<String> cmds)  {
+               String machines;
+               if(cmds.size()>0) {
+                       machines = cmds.removeFirst();
+               } else {
+                       machines = AAFSSO.cons.readLine("Machines (sep by ','): ");
+               }
+               return Split.split(',', machines);
+       }
+
+       private static void createArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+
+               Artifacts artifacts = new Artifacts();
+               Artifact arti = new Artifact();
+               artifacts.getArtifact().add(arti);
+               arti.setMechid(mechID!=null?mechID:AAFSSO.cons.readLine("MechID: "));
+               arti.setMachine(machine!=null?machine:AAFSSO.cons.readLine("Machine (%s): ",InetAddress.getLocalHost().getHostName()));
+               arti.setCa(AAFSSO.cons.readLine("CA: (%s): ","aaf"));
+               
+               String resp = AAFSSO.cons.readLine("Types [file,jks,script] (%s): ", "jks");
+               for(String s : Split.splitTrim(',', resp)) {
+                       arti.getType().add(s);
+               }
+               // Always do Script
+               if(!resp.contains(SCRIPT)) {
+                       arti.getType().add(SCRIPT);
+               }
+
+               // Note: Sponsor is set on Creation by CM
+               String configRootName = FQI.reverseDomain(arti.getMechid());
+               arti.setNs(AAFSSO.cons.readLine("Namespace (%s): ",configRootName));
+               arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", System.getProperty("user.dir")));
+               arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", System.getProperty("user.name")));
+               arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renewal Days (%s):", "30")));
+               arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (mailto owner):", "")));
+               
+               TimeTaken tt = trans.start("Create Artifact", Env.REMOTE);
+               try {
+                       Future<Artifacts> future = aafcon.client(CM_VER).create("/cert/artifacts", artifactsDF, artifacts);
+                       if(future.get(TIMEOUT)) {
+                               trans.info().printf("Call to AAF Certman successful %s, %s",arti.getMechid(), arti.getMachine());
+                       } else {
+                               trans.error().printf("Call to AAF Certman failed, %s",
+                                       errMsg.toMsg(future));
+                       }
+               } finally {
+                       tt.done();
+               }
+       }
+
+       private static String toNotification(String notification) {
+               if(notification==null) {
+                       notification="";
+               } else if(notification.length()>0) {
+                       if(notification.indexOf(':')<0) {
+                               notification = "mailto:" + notification;
+                       }
+               }
+               return notification;
+       }
+       
+
+       private static void readArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+
+               TimeTaken tt = trans.start("Read Artifact", Env.SUB);
+               try {
+                       Future<Artifacts> future = aafcon.client(CM_VER)
+                                       .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF,"Authorization","Bearer " + trans.getProperty("oauth_token"));
+       
+                       if(future.get(TIMEOUT)) {
+                               boolean printed = false;
+                               for(Artifact a : future.value.getArtifact()) {
+                                       AAFSSO.cons.printf("MechID:          %s\n",a.getMechid()); 
+                                       AAFSSO.cons.printf("  Sponsor:       %s\n",a.getSponsor()); 
+                                       AAFSSO.cons.printf("Machine:         %s\n",a.getMachine()); 
+                                       AAFSSO.cons.printf("CA:              %s\n",a.getCa()); 
+                                       StringBuilder sb = new StringBuilder();
+                                       boolean first = true;
+                                       for(String t : a.getType()) {
+                                               if(first) {first=false;}
+                                               else{sb.append(',');}
+                                               sb.append(t);
+                                       }
+                                       AAFSSO.cons.printf("Types:           %s\n",sb);
+                                       AAFSSO.cons.printf("Namespace:       %s\n",a.getNs()); 
+                                       AAFSSO.cons.printf("Directory:       %s\n",a.getDir());
+                                       AAFSSO.cons.printf("O/S User:        %s\n",a.getOsUser());
+                                       AAFSSO.cons.printf("Renew Days:      %d\n",a.getRenewDays());
+                                       AAFSSO.cons.printf("Notification     %s\n",a.getNotification());
+                                       printed = true;
+                               }
+                               if(!printed) {
+                                       AAFSSO.cons.printf("Artifact for %s %s does not exist\n", mechID, machine);
+                               }
+                       } else {
+                               trans.error().log(errMsg.toMsg(future));
+                       }
+               } finally {
+                       tt.done();
+               }
+       }
+       
+       private static void copyArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+               String[] newmachs = machines(cmds);
+               if(newmachs==null || newmachs == null) {
+                       trans.error().log("No machines listed to copy to");
+               } else {
+                       TimeTaken tt = trans.start("Copy Artifact", Env.REMOTE);
+                       try {
+                               Future<Artifacts> future = aafcon.client(CM_VER)
+                                               .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF);
+                       
+                               if(future.get(TIMEOUT)) {
+                                       boolean printed = false;
+                                       for(Artifact a : future.value.getArtifact()) {
+                                               for(String m : newmachs) {
+                                                       a.setMachine(m);
+                                                       Future<Artifacts> fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, future.value);
+                                                       if(fup.get(TIMEOUT)) {
+                                                               trans.info().printf("Copy of %s %s successful to %s",mechID,machine,m);
+                                                       } else {
+                                                               trans.error().printf("Call to AAF Certman failed, %s",
+                                                                       errMsg.toMsg(fup));
+                                                       }
+       
+                                                       printed = true;
+                                               }
+                                       }
+                                       if(!printed) {
+                                               AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine);
+                                       }
+                               } else {
+                                       trans.error().log(errMsg.toMsg(future));
+                               }
+                       } finally {
+                               tt.done();
+                       }
+               }
+       }
+
+       private static void updateArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+
+               TimeTaken tt = trans.start("Update Artifact", Env.REMOTE);
+               try {
+                       Future<Artifacts> fread = aafcon.client(CM_VER)
+                                       .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF);
+       
+                       if(fread.get(TIMEOUT)) {
+                               Artifacts artifacts = new Artifacts();
+                               for(Artifact a : fread.value.getArtifact()) {
+                                       Artifact arti = new Artifact();
+                                       artifacts.getArtifact().add(arti);
+                                       
+                                       AAFSSO.cons.printf("For %s on %s\n", a.getMechid(),a.getMachine());
+                                       arti.setMechid(a.getMechid());
+                                       arti.setMachine(a.getMachine());
+                                       arti.setCa(AAFSSO.cons.readLine("CA: (%s): ",a.getCa()));
+                                       StringBuilder sb = new StringBuilder();
+                                       boolean first = true;
+                                       for(String t : a.getType()) {
+                                               if(first) {first=false;}
+                                               else{sb.append(',');}
+                                               sb.append(t);
+                                       }
+       
+                                       String resp = AAFSSO.cons.readLine("Types [file,jks,pkcs12] (%s): ", sb);
+                                       for(String s : Split.splitTrim(',', resp)) {
+                                               arti.getType().add(s);
+                                       }
+                                       // Always do Script
+                                       if(!resp.contains(SCRIPT)) {
+                                               arti.getType().add(SCRIPT);
+                                       }
+
+                                       // Note: Sponsor is set on Creation by CM
+                                       arti.setNs(AAFSSO.cons.readLine("Namespace (%s): ",a.getNs()));
+                                       arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", a.getDir()));
+                                       arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", a.getOsUser()));
+                                       arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renew Days (%s):", a.getRenewDays())));
+                                       arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (%s):", a.getNotification())));
+       
+                               }
+                               if(artifacts.getArtifact().size()==0) {
+                                       AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine);
+                               } else {
+                                       Future<Artifacts> fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, artifacts);
+                                       if(fup.get(TIMEOUT)) {
+                                               trans.info().printf("Call to AAF Certman successful %s, %s",mechID,machine);
+                                       } else {
+                                               trans.error().printf("Call to AAF Certman failed, %s",
+                                                       errMsg.toMsg(fup));
+                                       }
+                               }
+                       } else {
+                               trans.error().printf("Call to AAF Certman failed, %s %s, %s",
+                                               errMsg.toMsg(fread),mechID,machine);
+                       }
+               } finally {
+                       tt.done();
+               }
+       }
+       
+       private static void deleteArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechid = mechID(cmds);
+               String machine = machine(cmds);
+               
+               TimeTaken tt = trans.start("Delete Artifact", Env.REMOTE);
+               try {
+                       Future<Void> future = aafcon.client(CM_VER)
+                                       .delete("/cert/artifacts/"+mechid+"/"+machine,"application/json" );
+       
+                       if(future.get(TIMEOUT)) {
+                               trans.info().printf("Call to AAF Certman successful %s, %s",mechid,machine);
+                       } else {
+                               trans.error().printf("Call to AAF Certman failed, %s %s, %s",
+                                       errMsg.toMsg(future),mechid,machine);
+                       }
+               } finally {
+                       tt.done();
+               }
+       }
+
+       
+
+       private static boolean placeCerts(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               boolean rv = false;
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+               String[] fqdns = Split.split(':', machine);
+               String key;
+               if(fqdns.length>1) {
+                       key = fqdns[0];
+                       machine = fqdns[1];
+               } else {
+                       key = machine;
+               }
+               
+               TimeTaken tt = trans.start("Place Artifact", Env.REMOTE);
+               try {
+                       Future<Artifacts> acf = aafcon.client(CM_VER)
+                                       .read("/cert/artifacts/"+mechID+'/'+key, artifactsDF);
+                       if(acf.get(TIMEOUT)) {
+                               if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) {
+                                       AAFSSO.cons.printf("===> There are no artifacts for %s on machine '%s'\n", mechID, key);
+                               } else {
+                                       for(Artifact a : acf.value.getArtifact()) {
+                                               String osID = System.getProperty("user.name");
+                                               if(a.getOsUser().equals(osID)) {
+                                                       CertificateRequest cr = new CertificateRequest();
+                                                       cr.setMechid(a.getMechid());
+                                                       cr.setSponsor(a.getSponsor());
+                                                       for(int i=0;i<fqdns.length;++i) {
+                                                               cr.getFqdns().add(fqdns[i]);
+                                                       }
+                                                       Future<String> f = aafcon.client(CM_VER)
+                                                                       .setQueryParams("withTrust")
+                                                                       .updateRespondString("/cert/" + a.getCa(),reqDF, cr);
+                                                       if(f.get(TIMEOUT)) {
+                                                               CertInfo capi = certDF.newData().in(TYPE.JSON).load(f.body()).asObject();
+                                                               for(String type : a.getType()) {
+                                                                       PlaceArtifact pa = placeArtifact.get(type);
+                                                                       if(pa!=null) {
+                                                                               if(rv = pa.place(trans, capi, a,machine)) {
+                                                                                       notifyPlaced(a,rv);
+                                                                               }
+                                                                       }
+                                                               }
+                                                               // Cover for the above multiple pass possibilities with some static Data, then clear per Artifact
+                                                       } else {
+                                                               trans.error().log(errMsg.toMsg(f));
+                                                       }
+                                               } else {
+                                                       trans.error().log("You must be OS User \"" + a.getOsUser() +"\" to place Certificates on this box");
+                                               }
+                                       }
+                               }
+                       } else {
+                               trans.error().log(errMsg.toMsg(acf));
+                       }
+               } finally {
+                       tt.done();
+               }
+               return rv;
+       }
+       
+       private static void notifyPlaced(Artifact a, boolean rv) {
+       }
+
+       private static void showPass(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+
+               TimeTaken tt = trans.start("Show Password", Env.REMOTE);
+               try {
+                       Future<Artifacts> acf = aafcon.client(CM_VER)
+                                       .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF);
+                       if(acf.get(TIMEOUT)) {
+                               // Have to wait for JDK 1.7 source...
+                               //switch(artifact.getType()) {
+                               if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) {
+                                       AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine);
+                               } else {
+                                       String id = aafcon.defID();
+                                       boolean allowed;
+                                       for(Artifact a : acf.value.getArtifact()) {
+                                               allowed = id!=null && (id.equals(a.getSponsor()) ||
+                                                               (id.equals(a.getMechid()) 
+                                                                               && aafcon.securityInfo().defSS.getClass().isAssignableFrom(HBasicAuthSS.class)));
+                                               if(!allowed) {
+                                                       Future<String> pf = aafcon.client(CM_VER).read("/cert/may/" + 
+                                                                       a.getNs() + ".certman|"+a.getCa()+"|showpass","*/*");
+                                                       if(pf.get(TIMEOUT)) {
+                                                               allowed = true;
+                                                       } else {
+                                                               trans.error().log(errMsg.toMsg(pf));
+                                                       }
+                                               }
+                                               if(allowed) {
+                                                       File dir = new File(a.getDir());
+                                                       Properties props = new Properties();
+                                                       FileInputStream fis = new FileInputStream(new File(dir,a.getNs()+".props"));
+                                                       try {
+                                                               props.load(fis);
+                                                               fis.close();
+                                                               fis = new FileInputStream(new File(dir,a.getNs()+".chal"));
+                                                               props.load(fis);
+                                                       } finally {
+                                                               fis.close();
+                                                       }
+                                                       
+                                                       File f = new File(dir,a.getNs()+".keyfile");
+                                                       if(f.exists()) {
+                                                               Symm symm = Symm.obtain(f);
+                                                               
+                                                               for(Iterator<Entry<Object,Object>> iter = props.entrySet().iterator(); iter.hasNext();) {
+                                                                       Entry<Object,Object> en = iter.next();
+                                                                       if(en.getValue().toString().startsWith("enc:")) {
+                                                                               System.out.printf("%s=%s\n", en.getKey(), symm.depass(en.getValue().toString()));
+                                                                       }
+                                                               }
+                                                       } else {
+                                                               trans.error().printf("%s.keyfile must exist to read passwords for %s on %s",
+                                                                               f.getAbsolutePath(),a.getMechid(), a.getMachine());
+                                                       }
+                                               }
+                                       }
+                               }
+                       } else {
+                               trans.error().log(errMsg.toMsg(acf));
+                       }
+               } finally {
+                       tt.done();
+               }
+
+       }
+       
+
+       /**
+        * Check returns Error Codes, so that Scripts can know what to do
+        * 
+        *   0 - Check Complete, nothing to do
+        *   1 - General Error
+        *   2 - Error for specific Artifact - read check.msg
+        *   10 - Certificate Updated - check.msg is email content
+        *   
+        * @param trans
+        * @param aafcon
+        * @param cmds
+        * @return
+        * @throws Exception
+        */
+       private static int check(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception {
+               int exitCode=1;
+               String mechID = mechID(cmds);
+               String machine = machine(cmds);
+               
+               TimeTaken tt = trans.start("Check Certificate", Env.REMOTE);
+               try {
+               
+                       Future<Artifacts> acf = aafcon.client(CM_VER)
+                                       .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF);
+                       if(acf.get(TIMEOUT)) {
+                               // Have to wait for JDK 1.7 source...
+                               //switch(artifact.getType()) {
+                               if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) {
+                                       AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine);
+                               } else {
+                                       String id = aafcon.defID();
+                                       GregorianCalendar now = new GregorianCalendar();
+                                       for(Artifact a : acf.value.getArtifact()) {
+                                               if(id.equals(a.getMechid())) {
+                                                       File dir = new File(a.getDir());
+                                                       Properties props = new Properties();
+                                                       FileInputStream fis = new FileInputStream(new File(dir,a.getNs()+".props"));
+                                                       try {
+                                                               props.load(fis);
+                                                       } finally {
+                                                               fis.close();
+                                                       }
+                                                       
+                                                       String prop;                                            
+                                                       File f;
+       
+                                                       if((prop=props.getProperty(Config.CADI_KEYFILE))==null ||
+                                                               !(f=new File(prop)).exists()) {
+                                                                       trans.error().printf("Keyfile must exist to check Certificates for %s on %s",
+                                                                               a.getMechid(), a.getMachine());
+                                                       } else {
+                                                               String ksf = props.getProperty(Config.CADI_KEYSTORE);
+                                                               String ksps = props.getProperty(Config.CADI_KEYSTORE_PASSWORD);
+                                                               if(ksf==null || ksps == null) {
+                                                                       trans.error().printf("Properties %s and %s must exist to check Certificates for %s on %s",
+                                                                                       Config.CADI_KEYSTORE, Config.CADI_KEYSTORE_PASSWORD,a.getMechid(), a.getMachine());
+                                                               } else {
+                                                                       KeyStore ks = KeyStore.getInstance("JKS");
+                                                                       Symm symm = Symm.obtain(f);
+                                                                       
+                                                                       fis = new FileInputStream(ksf);
+                                                                       try {
+                                                                               ks.load(fis,symm.depass(ksps).toCharArray());
+                                                                       } finally {
+                                                                               fis.close();
+                                                                       }
+                                                                       X509Certificate cert = (X509Certificate)ks.getCertificate(mechID);
+                                                                       String msg = null;
+
+                                                                       if(cert==null) {
+                                                                               msg = String.format("X509Certificate does not exist for %s on %s in %s",
+                                                                                               a.getMechid(), a.getMachine(), ksf);
+                                                                               trans.error().log(msg);
+                                                                               exitCode = 2;
+                                                                       } else {
+                                                                               GregorianCalendar renew = new GregorianCalendar();
+                                                                               renew.setTime(cert.getNotAfter());
+                                                                               renew.add(GregorianCalendar.DAY_OF_MONTH,-1*a.getRenewDays());
+                                                                               if(renew.after(now)) {
+                                                                                       msg = String.format("X509Certificate for %s on %s has been checked on %s. It expires on %s; it will not be renewed until %s.\n", 
+                                                                                                       a.getMechid(), a.getMachine(),Chrono.dateOnlyStamp(now),cert.getNotAfter(),Chrono.dateOnlyStamp(renew));
+                                                                                       trans.info().log(msg);
+                                                                                       exitCode = 0; // OK
+                                                                               } else {
+                                                                                       trans.info().printf("X509Certificate for %s on %s expiration, %s, needs Renewal.\n", 
+                                                                                                       a.getMechid(), a.getMachine(),cert.getNotAfter());
+                                                                                       cmds.offerLast(mechID);
+                                                                                       cmds.offerLast(machine);
+                                                                                       if(placeCerts(trans,aafcon,cmds)) {
+                                                                                               msg = String.format("X509Certificate for %s on %s has been renewed. Ensure services using are refreshed.\n", 
+                                                                                                               a.getMechid(), a.getMachine());
+                                                                                               exitCode = 10; // Refreshed
+                                                                                       } else {
+                                                                                               msg = String.format("X509Certificate for %s on %s attempted renewal, but failed. Immediate Investigation is required!\n", 
+                                                                                                               a.getMechid(), a.getMachine());
+                                                                                               exitCode = 1; // Error Renewing
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                                       if(msg!=null) {
+                                                                               FileOutputStream fos = new FileOutputStream(a.getDir()+'/'+a.getNs()+".msg");
+                                                                               try {
+                                                                                       fos.write(msg.getBytes());
+                                                                               } finally {
+                                                                                       fos.close();
+                                                                               }
+                                                                       }
+                                                               }
+                                                               
+                                                       }
+                                               }
+                                       }
+                               }
+                       } else {
+                               trans.error().log(errMsg.toMsg(acf));
+                               exitCode=1;
+                       }
+               } finally {
+                       tt.done();
+               }
+               return exitCode;
+       }
+
+}
+                       
+               
+
+
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java
new file mode 100644 (file)
index 0000000..7011188
--- /dev/null
@@ -0,0 +1,447 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Collection;
+import java.util.List;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.Trans;
+
+public class Factory {
+       private static final String PRIVATE_KEY_HEADER = "PRIVATE KEY";
+       public static final String KEY_ALGO = "RSA";
+       public static final String SIG_ALGO = "SHA256withRSA";
+
+       public  static final int KEY_LENGTH = 2048;
+       private static final KeyPairGenerator keygen;
+       private static final KeyFactory keyFactory;
+       private static final CertificateFactory certificateFactory;
+       private static final SecureRandom random;
+       
+       
+       private static final Symm base64 = Symm.base64.copy(64);
+
+       static {
+                       random = new SecureRandom();
+                       KeyPairGenerator tempKeygen;
+                       try {
+                               tempKeygen = KeyPairGenerator.getInstance(KEY_ALGO);//,"BC");
+                               tempKeygen.initialize(KEY_LENGTH, random);
+                       } catch (NoSuchAlgorithmException e) {
+                               tempKeygen = null;
+                               e.printStackTrace(System.err);
+                       }
+                       keygen = tempKeygen;
+
+                       KeyFactory tempKeyFactory;
+                       try {
+                               tempKeyFactory=KeyFactory.getInstance(KEY_ALGO);//,"BC"
+                       } catch (NoSuchAlgorithmException e) {
+                               tempKeyFactory = null;
+                               e.printStackTrace(System.err);
+                       };
+                       keyFactory = tempKeyFactory;
+                        
+                       CertificateFactory tempCertificateFactory;
+                       try {
+                               tempCertificateFactory = CertificateFactory.getInstance("X.509");
+                       } catch (CertificateException e) {
+                               tempCertificateFactory = null;
+                               e.printStackTrace(System.err);
+                       }
+                       certificateFactory = tempCertificateFactory;
+
+                
+       }
+
+
+       public static KeyPair generateKeyPair(Trans trans) {
+               TimeTaken tt;
+               if(trans!=null) {
+                       tt = trans.start("Generate KeyPair", Env.SUB);
+               } else {
+                       tt = null;
+               }
+               try {
+                       return keygen.generateKeyPair();
+               } finally {
+                       if(tt!=null) {
+                               tt.done();
+                       }
+               }
+       }  
+
+       private static final String LINE_END = "-----\n";
+
+       protected static String textBuilder(String kind, byte[] bytes) throws IOException {
+               StringBuilder sb = new StringBuilder();
+               sb.append("-----BEGIN ");
+               sb.append(kind);
+               sb.append(LINE_END);
+
+               ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               base64.encode(bais, baos);
+               sb.append(new String(baos.toByteArray()));
+               
+               if(sb.charAt(sb.length()-1)!='\n') {
+                       sb.append('\n');
+               }
+               sb.append("-----END ");
+               sb.append(kind);
+               sb.append(LINE_END);
+               return sb.toString();
+       }
+       
+       public static PrivateKey toPrivateKey(Trans trans, String pk) throws IOException, CertException {
+               byte[] bytes = decode(new StringReader(pk));
+               return toPrivateKey(trans, bytes);
+       }
+       
+       public static PrivateKey toPrivateKey(Trans trans, byte[] bytes) throws IOException, CertException {
+               TimeTaken tt=trans.start("Reconstitute Private Key", Env.SUB);
+               try {
+                       return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
+               } catch (InvalidKeySpecException e) {
+                       throw new CertException("Translating Private Key from PKCS8 KeySpec",e);
+               } finally {
+                       tt.done();
+               }
+       }
+       
+       public static PrivateKey toPrivateKey(Trans trans, File file) throws IOException, CertException {
+               TimeTaken tt = trans.start("Decode Private Key File", Env.SUB);
+               try {
+                       return toPrivateKey(trans,decode(file));
+               }finally {
+                       tt.done();
+               }
+       }
+
+       public static String toString(Trans trans, PrivateKey pk) throws IOException {
+//             PKCS8EncodedKeySpec pemContents = new PKCS8EncodedKeySpec(pk.getEncoded());
+               trans.debug().log("Private Key to String");
+               return textBuilder(PRIVATE_KEY_HEADER,pk.getEncoded());
+       }
+
+       public static PublicKey toPublicKey(Trans trans, String pk) throws IOException {
+               TimeTaken tt = trans.start("Reconstitute Public Key", Env.SUB);
+               try {
+                       ByteArrayInputStream bais = new ByteArrayInputStream(pk.getBytes());
+                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                       Symm.base64noSplit.decode(bais, baos);
+
+                       return keyFactory.generatePublic(new X509EncodedKeySpec(baos.toByteArray()));
+               } catch (InvalidKeySpecException e) {
+                       trans.error().log(e,"Translating Public Key from X509 KeySpec");
+                       return null;
+               } finally {
+                       tt.done();
+               }
+       }
+       
+       public static String toString(Trans trans, PublicKey pk) throws IOException {
+               trans.debug().log("Public Key to String");
+               return textBuilder("PUBLIC KEY",pk.getEncoded());
+       }
+
+       public static Collection<? extends Certificate> toX509Certificate(String x509) throws CertificateException {
+               return toX509Certificate(x509.getBytes());
+       }
+       
+       public static Collection<? extends Certificate> toX509Certificate(List<String> x509s) throws CertificateException {
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               try {
+                       for(String x509 : x509s) {
+                               baos.write(x509.getBytes());
+                       }
+               } catch (IOException e) {
+                       throw new CertificateException(e);
+               }
+               return toX509Certificate(new ByteArrayInputStream(baos.toByteArray()));
+       }
+
+       public static Collection<? extends Certificate> toX509Certificate(byte[] x509) throws CertificateException {
+               return certificateFactory.generateCertificates(new ByteArrayInputStream(x509));
+       }
+
+       public static Collection<? extends Certificate> toX509Certificate(Trans trans, File file) throws CertificateException, FileNotFoundException {
+               FileInputStream fis = new FileInputStream(file);
+               try {
+                       return toX509Certificate(fis);
+               } finally {
+                       try {
+                               fis.close();
+                       } catch (IOException e) {
+                               throw new CertificateException(e);
+                       }
+               }
+       }
+
+       public static Collection<? extends Certificate> toX509Certificate(InputStream is) throws CertificateException {
+               return certificateFactory.generateCertificates(is);
+       }
+
+       public static String toString(Trans trans, Certificate cert) throws IOException, CertException {
+               if(trans.debug().isLoggable()) {
+                       StringBuilder sb = new StringBuilder("Certificate to String");
+                       if(cert instanceof X509Certificate) {
+                               sb.append(" - ");
+                               sb.append(((X509Certificate)cert).getSubjectDN());
+                       }
+                       trans.debug().log(sb);
+               }
+               try {
+                       if(cert==null) {
+                               throw new CertException("Certificate not built");
+                       }
+                       return textBuilder("CERTIFICATE",cert.getEncoded());
+               } catch (CertificateEncodingException e) {
+                       throw new CertException(e);
+               }
+       }
+
+       public static Cipher pkCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
+               return Cipher.getInstance(KEY_ALGO); 
+       }
+
+       public static Cipher pkCipher(Key key, boolean encrypt) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
+               Cipher cipher = Cipher.getInstance(KEY_ALGO);
+               cipher.init(encrypt?Cipher.ENCRYPT_MODE:Cipher.DECRYPT_MODE,key);
+               return cipher;
+       }
+
+       public static byte[] strip(Reader rdr) throws IOException {
+               BufferedReader br = new BufferedReader(rdr);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               String line;
+               while((line=br.readLine())!=null) {
+                       if(line.length()>0 &&
+                          !line.startsWith("-----") &&
+                          line.indexOf(':')<0) {  // Header elements
+                               baos.write(line.getBytes());
+                       }
+               }
+               return baos.toByteArray();
+       }
+       
+       public static class StripperInputStream extends InputStream {
+               private Reader created;
+               private BufferedReader br;
+               private int idx;
+               private String line;
+
+               public StripperInputStream(Reader rdr) {
+                       if(rdr instanceof BufferedReader) {
+                               br = (BufferedReader)rdr;
+                       } else {
+                               br = new BufferedReader(rdr);
+                       }
+                       created = null;
+               }
+               
+               public StripperInputStream(File file) throws FileNotFoundException {
+                       this(new FileReader(file));
+                       created = br;
+               }
+
+               public StripperInputStream(InputStream is) throws FileNotFoundException {
+                       this(new InputStreamReader(is));
+                       created = br;
+               }
+
+               @Override
+               public int read() throws IOException {
+                       if(line==null || idx>=line.length()) {
+                               while((line=br.readLine())!=null) {
+                                       if(line.length()>0 &&
+                                          !line.startsWith("-----") &&
+                                          line.indexOf(':')<0) {  // Header elements
+                                               break;
+                                       }
+                               }
+
+                               if(line==null) {
+                                       return -1;
+                               }
+                               idx = 0;
+                       }
+                       return line.charAt(idx++);
+               }
+
+               /* (non-Javadoc)
+                * @see java.io.InputStream#close()
+                */
+               @Override
+               public void close() throws IOException {
+                       if(created!=null) {
+                               created.close();
+                       }
+               }
+       }
+
+       public static class Base64InputStream extends InputStream {
+               private InputStream created;
+               private InputStream is;
+               private byte trio[];
+               private byte duo[];
+               private int idx;
+
+               
+               public Base64InputStream(File file) throws FileNotFoundException {
+                       this(new FileInputStream(file));
+                       created = is;
+               }
+
+               public Base64InputStream(InputStream is) throws FileNotFoundException {
+                       this.is = is;
+                       trio = new byte[3];
+                       idx = 4;
+               }
+
+               @Override
+               public int read() throws IOException {
+                       if(duo==null || idx>=duo.length) {
+                               int read = is.read(trio);
+                               if(read==-1) {
+                                       return -1;
+                               }
+                               duo = Symm.base64.decode(trio);
+                               if(duo==null || duo.length==0) {
+                                       return -1;
+                               }
+                               idx=0;
+                       }
+                       
+                       return duo[idx++];
+               }
+
+               /* (non-Javadoc)
+                * @see java.io.InputStream#close()
+                */
+               @Override
+               public void close() throws IOException {
+                       if(created!=null) {
+                               created.close();
+                       }
+               }
+       }
+
+       public static byte[] decode(byte[] bytes) throws IOException {
+               ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               Symm.base64.decode(bais, baos);
+               return baos.toByteArray();
+       }
+       
+       public static byte[] decode(File f) throws IOException {
+               FileReader fr = new FileReader(f);
+               try {
+                       return Factory.decode(fr);
+               } finally {
+                       fr.close();
+               }
+
+       }
+       public static byte[] decode(Reader rdr) throws IOException {
+               return decode(strip(rdr));
+       }
+
+
+       public static byte[] binary(File file) throws IOException {
+               DataInputStream dis = new DataInputStream(new FileInputStream(file));
+               try {
+                       byte[] bytes = new byte[(int)file.length()];
+                       dis.readFully(bytes);
+                       return bytes;
+               } finally {
+                       dis.close();
+               }
+       }
+
+
+       public static byte[] sign(Trans trans, byte[] bytes, PrivateKey pk) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
+               TimeTaken tt = trans.start("Sign Data", Env.SUB);
+               try {
+                       Signature sig = Signature.getInstance(SIG_ALGO);
+                       sig.initSign(pk, random);
+                       sig.update(bytes);
+                       return sig.sign();
+               } finally {
+                       tt.done();
+               }
+       }
+
+       public static String toSignatureString(byte[] signed) throws IOException {
+               return textBuilder("SIGNATURE", signed);
+       }
+
+       public static boolean verify(Trans trans, byte[] bytes, byte[] signature, PublicKey pk) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+               TimeTaken tt = trans.start("Verify Data", Env.SUB);
+               try {
+                       Signature sig = Signature.getInstance(SIG_ALGO);
+                       sig.initVerify(pk);
+                       sig.update(bytes);
+                       return sig.verify(signature);
+               } finally {
+                       tt.done();
+               }       
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java
new file mode 100644 (file)
index 0000000..369f48d
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.misc.env.Trans;
+
+public interface PlaceArtifact {
+       public boolean place(Trans trans, CertInfo cert, Artifact arti, String machine) throws CadiException;
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java
new file mode 100644 (file)
index 0000000..f419577
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.File;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.util.Chmod;
+import org.onap.aaf.misc.env.Trans;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+public class PlaceArtifactInFiles extends ArtifactDir {
+       @Override
+       public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException {
+               try {
+                       // Setup Public Cert
+                       File f = new File(dir,arti.getNs()+".crt");
+                       write(f,Chmod.to644,certInfo.getCerts().get(0),C_R);
+                       
+                       // Setup Private Key
+                       f = new File(dir,arti.getNs()+".key");
+                       write(f,Chmod.to400,certInfo.getPrivatekey(),C_R);
+                       
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+               return true;
+       }
+}
+
+
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java
new file mode 100644 (file)
index 0000000..a4d095e
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.util.Chmod;
+import org.onap.aaf.misc.env.Trans;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+public class PlaceArtifactInKeystore extends ArtifactDir {
+       private String kst;
+       //TODO get ROOT DNs or Trusted DNs from Certificate Manager.
+//     private static String[] rootDNs = new String[]{                 
+//                     "CN=ATT CADI Root CA - Test, O=ATT, OU=CSO, C=US", // Lab.  delete eventually
+//                     "CN=ATT AAF CADI TEST CA, OU=CSO, O=ATT, C=US",
+//                     "CN=ATT AAF CADI CA, OU=CSO, O=ATT, C=US"
+//     };
+
+       public PlaceArtifactInKeystore(String kst) {
+               this.kst = kst;
+       }
+
+       @Override
+       public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException {
+               File fks = new File(dir,arti.getNs()+'.'+kst);
+               try {
+                       KeyStore jks = KeyStore.getInstance(kst);
+                       if(fks.exists()) {
+                               fks.delete();
+                       }       
+
+                       // Get the Cert(s)... Might include Trust store
+                       Collection<? extends Certificate> certColl = Factory.toX509Certificate(certInfo.getCerts());
+                       // find where the trusts end in 1.0 API
+               
+                       X509Certificate x509;
+                       List<X509Certificate> certList = new ArrayList<X509Certificate>();
+                       Certificate[] trustChain = null;
+                       Certificate[] trustCAs;
+                       for(Certificate c : certColl) {
+                               x509 = (X509Certificate)c;
+                               if(trustChain==null && x509.getSubjectDN().equals(x509.getIssuerDN())) {
+                                       trustChain = new Certificate[certList.size()];
+                                       certList.toArray(trustChain);
+                                       certList.clear(); // reuse
+                               }
+                               certList.add(x509);
+                       }
+                       
+                       // remainder should be Trust CAs
+                       trustCAs = new Certificate[certList.size()];
+                       certList.toArray(trustCAs);
+
+                       // Properties, etc
+                       // Add CADI Keyfile Entry to Properties
+                       addProperty(Config.CADI_KEYFILE,arti.getDir()+'/'+arti.getNs() + ".keyfile");
+                       // Set Keystore Password
+                       addProperty(Config.CADI_KEYSTORE,fks.getAbsolutePath());
+                       String keystorePass = Symm.randomGen(CmAgent.PASS_SIZE);
+                       addEncProperty(Config.CADI_KEYSTORE_PASSWORD,keystorePass);
+                       char[] keystorePassArray = keystorePass.toCharArray();
+                       jks.load(null,keystorePassArray); // load in
+                       
+                       // Add Private Key/Cert Entry for App
+                       // Note: Java SSL security classes, while having a separate key from keystore,
+                       // is documented to not actually work. 
+                       // java.security.UnrecoverableKeyException: Cannot recover key
+                       // You can create a custom Key Manager to make it work, but Practicality  
+                       // dictates that you live with the default, meaning, they are the same
+                       String keyPass = keystorePass; //Symm.randomGen(CmAgent.PASS_SIZE);
+                       PrivateKey pk = Factory.toPrivateKey(trans, certInfo.getPrivatekey());
+                       addEncProperty(Config.CADI_KEY_PASSWORD, keyPass);
+                       addProperty(Config.CADI_ALIAS, arti.getMechid());
+//                     Set<Attribute> attribs = new HashSet<Attribute>();
+//                     if(kst.equals("pkcs12")) {
+//                             // Friendly Name
+//                             attribs.add(new PKCS12Attribute("1.2.840.113549.1.9.20", arti.getNs()));
+//                     } 
+//                     
+                       KeyStore.ProtectionParameter protParam = 
+                                       new KeyStore.PasswordProtection(keyPass.toCharArray());
+                       
+                       KeyStore.PrivateKeyEntry pkEntry = 
+                               new KeyStore.PrivateKeyEntry(pk, trustChain);
+                       jks.setEntry(arti.getMechid(), 
+                                       pkEntry, protParam);
+
+                       // Write out
+                       write(fks,Chmod.to400,jks,keystorePassArray);
+                       
+                       // Change out to TrustStore
+                       fks = new File(dir,arti.getNs()+".trust."+kst);
+                       jks = KeyStore.getInstance(kst);
+                       
+                       // Set Truststore Password
+                       addProperty(Config.CADI_TRUSTSTORE,fks.getAbsolutePath());
+                       String trustStorePass = Symm.randomGen(CmAgent.PASS_SIZE);
+                       addEncProperty(Config.CADI_TRUSTSTORE_PASSWORD,trustStorePass);
+                       char[] truststorePassArray = trustStorePass.toCharArray();
+                       jks.load(null,truststorePassArray); // load in
+                       
+                       // Add Trusted Certificates
+                       for(int i=0; i<trustCAs.length;++i) {
+                               jks.setCertificateEntry("ca_" + arti.getCa() + '_' + i, trustCAs[i]);
+                       }
+                       // Write out
+                       write(fks,Chmod.to644,jks,truststorePassArray);
+
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+               return false;
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java
new file mode 100644 (file)
index 0000000..1ae5be9
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.PrintStream;
+
+import org.onap.aaf.misc.env.Trans;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+public class PlaceArtifactOnStream implements PlaceArtifact {
+       private PrintStream out;
+
+       public PlaceArtifactOnStream(PrintStream printStream) {
+               out = printStream;
+       }
+
+       @Override
+       public boolean place(Trans trans, CertInfo capi, Artifact a, String machine) {
+               if(capi.getNotes()!=null && capi.getNotes().length()>0) {
+                       trans.info().printf("Warning:    %s\n",capi.getNotes());
+               }
+               out.printf("Challenge:  %s\n",capi.getChallenge());
+               out.printf("PrivateKey:\n%s\n",capi.getPrivatekey());
+               out.println("Certificate Chain:");
+               for(String c : capi.getCerts()) {
+                       out.println(c);
+               }
+               return true;
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java
new file mode 100644 (file)
index 0000000..9347f70
--- /dev/null
@@ -0,0 +1,157 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.cm;
+
+import java.io.File;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.util.Chmod;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.misc.env.util.Split;
+
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.CertInfo;
+
+public class PlaceArtifactScripts extends ArtifactDir {
+       @Override
+       public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException {
+               try {
+                       // Setup check.sh script
+                       String filename = arti.getNs()+".check.sh";
+                       File f1 = new File(dir,filename);
+                       String email = arti.getNotification() + '\n';
+                       if(email.startsWith("mailto:")) {
+                               email=email.substring(7);
+                       }  else {
+                               email=arti.getOsUser() + '\n';
+                       }
+                       
+                       StringBuilder classpath = new StringBuilder();
+                       boolean first = true;
+                       for(String pth : Split.split(File.pathSeparatorChar, System.getProperty("java.class.path"))) {
+                               if(first) {
+                                       first=false;
+                               } else {
+                                       classpath.append(File.pathSeparatorChar);
+                               }
+                               File f = new File(pth);
+                               classpath.append(f.getCanonicalPath().replaceAll("[0-9]+\\.[0-9]+\\.[0-9]+","*"));
+                       }
+                       
+                       write(f1,Chmod.to644,
+                                       "#!/bin/bash " + f1.getCanonicalPath()+'\n',
+                                       "# Certificate Manager Check Script\n",
+                                       "# Check on Certificate, and renew if needed.\n",
+                                       "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n',
+                                       "DIR="+arti.getDir()+'\n',
+                                       "APP="+arti.getNs()+'\n',
+                                       "EMAIL="+email,
+                                       "CP=\""+classpath.toString()+"\"\n",
+                                       checkScript
+                                       );
+                       
+                       // Setup check.sh script
+                       File f2 = new File(dir,arti.getNs()+".crontab.sh");
+                       write(f2,Chmod.to644,
+                                       "#!/bin/bash " + f2.getCanonicalPath()+'\n',
+                                       "# Certificate Manager Crontab Loading Script\n",
+                                       "# Add/Update a Crontab entry, that adds a check on Certificate Manager generated Certificate nightly.\n",
+                                       "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n',
+                                       "TFILE=\"/tmp/cmcron$$.temp\"\n",
+                                       "DIR=\""+arti.getDir()+"\"\n",
+                                       "CF=\""+arti.getNs()+" Certificate Check Script\"\n",
+                                       "SCRIPT=\""+f1.getCanonicalPath()+"\"\n",
+                                       cronScript
+                                       );
+
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+               return true;
+       }
+       
+       /**
+        * Note: java.home gets Absolute Path of Java, where we probably want soft links from 
+        * JAVA_HOME
+        * @return
+        */
+       private final static String javaHome() {
+               String rc = System.getenv("JAVA_HOME");
+               return rc==null?System.getProperty("java.home"):rc;
+       }
+       private final static String checkScript = 
+                       "> $DIR/$APP.msg\n\n" +
+                       "function mailit {\n" +
+                       "  if [ -e /bin/mail ]; then\n" +
+                       "     MAILER=/bin/mail\n" +
+                       "  elif [ -e /usr/bin/mail ]; then \n" +
+                       "     MAILER=/usr/bin/mail\n" +
+                       "  else \n" +
+                       "     MAILER=\"\"\n" +
+                       "  fi\n" +
+                       " if [ \"$MAILER\" = \"\" ]; then\n" +
+                       "    printf \"$*\"\n" +
+                       " else \n" +
+                       "    printf \"$*\" | $MAILER -s \"AAF Certman Notification for `uname -n`\" $EMAIL\n"+
+                       " fi\n" +
+                       "}\n\n" +
+                       javaHome() + "/bin/" +"java -cp $CP " +
+                               CmAgent.class.getName() + 
+                               " cadi_prop_files=$DIR/$APP.props check 2>  $DIR/$APP.STDERR > $DIR/$APP.STDOUT\n" +
+                       "case \"$?\" in\n" +
+                       "  0)\n" +
+                       "    # Note: Validation will be mailed only the first day after any modification\n" +
+                       "    if [ \"`find $DIR -mtime 0 -name $APP.check.sh`\" != \"\" ] ; then\n" +
+                       "       mailit `echo \"Certficate Validated:\\n\\n\" | cat - $DIR/$APP.msg`\n" +
+                       "    else\n" +
+                       "       cat $DIR/$APP.msg\n" +
+                       "    fi\n" +
+                       "    ;;\n" +
+                       "  1) mailit \"Error with Certificate Check:\\\\n\\\\nCheck logs $DIR/$APP.STDOUT and $DIR/$APP.STDERR on `uname -n`\"\n" +
+                       "    ;;\n" +
+                       "  2) mailit `echo \"Certificate Check Error\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" +
+                       "    ;;\n" +
+                       "  10) mailit `echo \"Certificate Replaced\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" +
+                       "      if [ -e $DIR/$APP.restart.sh ]; then\n" +
+                       "        # Note: it is THIS SCRIPT'S RESPONSIBILITY to notify upon success or failure as necessary!!\n" +
+                       "        /bin/sh $DIR/$APP.restart.sh\n" +
+                       "      fi\n" +
+                       "    ;;\n" +
+                       "  *) mailit `echo \"Unknown Error code for CM Agent\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" +
+                       "    ;;\n" +
+                       " esac\n\n" +
+                       " # Note: make sure to cover this sripts' exit Code\n";
+       
+       private final static String cronScript = 
+                       "crontab -l | sed -n \"/#### BEGIN $CF/,/END $CF ####/!p\" > $TFILE\n" +
+                       "# Note: Randomize Minutes (0-60) and hours (1-4)\n" +
+                       "echo \"#### BEGIN $CF ####\" >> $TFILE\n" +
+                       "echo \"$(( $RANDOM % 60)) $(( $(( $RANDOM % 3 )) + 1 )) * * * /bin/bash $SCRIPT " +
+                               ">> $DIR/cronlog 2>&1 \" >> $TFILE\n" +
+                       "echo \"#### END $CF ####\" >> $TFILE\n" +
+                       "crontab $TFILE\n" +
+                       "rm $TFILE\n";
+}
+
+
+
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java
new file mode 100644 (file)
index 0000000..16bd866
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+import org.onap.aaf.cadi.Hash;
+
+public class AAFToken {
+       private static final int CAPACITY = (Long.SIZE*2+Byte.SIZE*3)/8;
+       private static final SecureRandom sr = new SecureRandom(); 
+
+       public static final String toToken(UUID uuid) {
+               long lsb = uuid.getLeastSignificantBits();
+               long msb = uuid.getMostSignificantBits();
+               int sum=35; // AAF
+               for(int i=0;i<Long.SIZE;i+=8) {
+                       sum+=((lsb>>i) & 0xFF);
+               }
+               for(int i=0;i<Long.SIZE;i+=8) {
+                       sum+=((((msb>>i) & 0xFF))<<0xB);
+               }
+               sum+=(sr.nextInt()&0xEFC00000); // this is just to not leave zeros laying around
+
+               ByteBuffer bb = ByteBuffer.allocate(CAPACITY);
+               bb.put((byte)sum);
+               bb.putLong(msb);
+               bb.put((byte)(sum>>8));
+               bb.putLong(lsb);
+               bb.put((byte)(sum>>16));
+               return Hash.toHexNo0x(bb.array());
+       }
+
+       public static final UUID fromToken(String token)  {
+               byte[] bytes = Hash.fromHexNo0x(token);
+               if(bytes==null) {
+                       return null;
+               }
+               ByteBuffer bb = ByteBuffer.wrap(bytes);
+               if(bb.capacity()!=CAPACITY ) {
+                       return null; // not a CADI Token
+               }
+               byte b1 = bb.get();
+               long msb = bb.getLong();
+               byte b2 = bb.get();
+               long lsb = bb.getLong();
+               byte b3 = (byte)(0x3F&bb.get());
+               int sum=35;
+               
+               for(int i=0;i<Long.SIZE;i+=8) {
+                       sum+=((lsb>>i) & 0xFF);
+               }
+               for(int i=0;i<Long.SIZE;i+=8) {
+                       sum+=((((msb>>i) & 0xFF))<<0xB);
+               }
+
+               if(b1!=((byte)sum) ||
+                  b2!=((byte)(sum>>8)) ||
+                  b3!=((byte)((sum>>16)))) {
+                       return null; // not a CADI Token                        
+               }
+               return new UUID(msb, lsb);
+       }
+       
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java
new file mode 100644 (file)
index 0000000..616e2dc
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Pool;
+import org.onap.aaf.misc.env.util.Pool.Creator;
+
+public abstract class AbsOTafLur {
+       protected static final String ERROR_GETTING_TOKEN_CLIENT = "Error getting TokenClient";
+       protected static final String REQUIRED_FOR_OAUTH2 = " is required for OAuth Access";
+
+       protected final TokenMgr tkMgr;
+       protected final PropAccess access;
+       protected final String client_id;
+       protected static Pool<TokenClient> tokenClientPool;
+       
+       protected AbsOTafLur(final PropAccess access, final String token_url, final String introspect_url) throws CadiException {
+               this.access = access;
+               if((client_id = access.getProperty(Config.AAF_APPID,null))==null) {
+                       throw new CadiException(Config.AAF_APPID + REQUIRED_FOR_OAUTH2);
+               }
+
+               synchronized(access) {
+                       if(tokenClientPool==null) {
+                               tokenClientPool = new Pool<TokenClient>(new TCCreator(access));
+                       }
+                       try {
+                               tkMgr = TokenMgr.getInstance(access, token_url, introspect_url);
+                       } catch (APIException e) {
+                               throw new CadiException("Unable to create TokenManager",e);
+                       }
+               }
+       }
+
+       private class TCCreator implements Creator<TokenClient> {
+               private TokenClientFactory tcf;
+               private final int timeout;
+               private final String url,enc_secret;
+               
+               public TCCreator(PropAccess access) throws CadiException { 
+                       try {
+                               tcf = TokenClientFactory.instance(access);
+                       } catch (APIException | GeneralSecurityException | IOException e1) {
+                               throw new CadiException(e1);
+                       }
+                       
+                       if((url = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL,null))==null) {
+                               throw new CadiException(Config.AAF_OAUTH2_TOKEN_URL + REQUIRED_FOR_OAUTH2);
+                       }
+                       
+                       try {
+                               timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
+                       } catch (NumberFormatException e) {
+                               throw new CadiException("Bad format for " + Config.AAF_CONN_TIMEOUT, e);
+                       }
+                       if((enc_secret= access.getProperty(Config.AAF_APPPASS,null))==null) {
+                               throw new CadiException(Config.AAF_APPPASS + REQUIRED_FOR_OAUTH2);
+                       }
+               }
+               
+               @Override
+               public TokenClient create() throws APIException {
+                       try {
+                               TokenClient tc = tcf.newClient(url, timeout);
+                               tc.client_creds(client_id, access.decrypt(enc_secret, true));
+                               return tc;
+                       } catch (CadiException | LocatorException | IOException e) {
+                               throw new APIException(e);
+                       }
+               }
+
+               @Override
+               public void destroy(TokenClient t) {
+               }
+
+               @Override
+               public boolean isValid(TokenClient t) {
+                       return t!=null && t.client_id()!=null;
+               }
+
+               @Override
+               public void reuse(TokenClient t) {
+               }
+       };
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#destroy()
+        */
+       public void destroy() {
+               tkMgr.close();
+       }
+       
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder)
+        */
+       public void clear(Principal p, StringBuilder report) {
+               tkMgr.clear(p, report);
+       }
+       
+
+       
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java
new file mode 100644 (file)
index 0000000..dc6fe39
--- /dev/null
@@ -0,0 +1,104 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.security.GeneralSecurityException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HAuthorizationHeader;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.APIException;
+
+public class HRenewingTokenSS extends HAuthorizationHeader {
+       private TokenClientFactory tcf;
+       private final TokenClient tc;
+       private final String[] scopes;
+       private final String tokenURL;
+       
+       public HRenewingTokenSS(final PropAccess access, final String tokenURL, final String ... nss) throws CadiException, IOException, GeneralSecurityException {
+               this(access,SecurityInfoC.instance(access, HttpURLConnection.class),tokenURL,nss);
+       }
+       
+       public HRenewingTokenSS(final PropAccess access, final SecurityInfoC<HttpURLConnection> si, final String tokenURL, final String ... nss) throws CadiException, IOException, GeneralSecurityException {
+               super(si,null,null/*Note: HeadValue overloaded */);
+               this.tokenURL = tokenURL;
+               try {
+                       tcf = TokenClientFactory.instance(access);
+                       tc = tcf.newClient(tokenURL);
+                       tc.client_creds(access);
+                       setUser(tc.client_id());
+                       String defaultNS = FQI.reverseDomain(tc.client_id());
+                       if(nss.length>0) {
+                               boolean hasDefault = false;
+                               for(String ns : nss) {
+                                       if(ns.equals(defaultNS)) {
+                                               hasDefault = true;
+                                       }
+                               }
+                               if(hasDefault) {
+                                       scopes=nss;             
+                               } else {
+                                       String[] nssPlus = new String[nss.length+1];
+                                       nssPlus[0]=defaultNS;
+                                       System.arraycopy(nss, 0, nssPlus, 1, nss.length);
+                                       scopes = nssPlus;
+                               }
+                       } else {
+                               scopes = new String[] {defaultNS};
+                       }
+
+               } catch (GeneralSecurityException | IOException | LocatorException | APIException e) {
+                       throw new CadiException(e);
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.client.AbsAuthentication#headValue()
+        */
+       @Override
+       protected String headValue() throws IOException {
+               Result<TimedToken> token;
+               try {
+                       token = tc.getToken(Kind.OAUTH,scopes);
+                       if(token.isOK()) {
+                               return "Bearer " + token.value.getAccessToken();
+                       } else {
+                               throw new IOException("Token cannot be obtained: " + token.code + '-' + token.error);
+                       }
+               } catch (IOException e) {
+                       throw e;
+               } catch (LocatorException | CadiException | APIException e) {
+                       throw new IOException(e);
+               }
+       }
+
+       public String tokenURL() {
+               return tokenURL;
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java
new file mode 100644 (file)
index 0000000..3d5f7d9
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.security.NoSuchAlgorithmException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Taf.LifeForm;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+import org.onap.aaf.misc.env.APIException;
+
+public class OAuth2HttpTaf implements HttpTaf {
+       final private Access access;
+       final private TokenMgr tmgr;
+
+       public OAuth2HttpTaf(final Access access, final TokenMgr tmgr) {
+               this.tmgr = tmgr;
+               this.access = access;
+       }
+       
+       @Override
+       public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
+               String authz = req.getHeader("Authorization");
+               if(authz != null && authz.length()>7 && authz.startsWith("Bearer ")) {
+                       if(!req.isSecure()) {
+                               access.log(Level.WARN,"WARNING! OAuth has been used over an insecure channel");
+                       }
+                       try {
+                               String tkn = authz.substring(7);
+                               Result<OAuth2Principal> rp = tmgr.toPrincipal(tkn,Hash.hashSHA256(tkn.getBytes()));
+                               if(rp.isOK()) {
+                                       return new OAuth2HttpTafResp(access,rp.value,rp.value.getName()+" authenticated by Bearer Token",RESP.IS_AUTHENTICATED,resp,false);
+                               } else {
+                                       return new OAuth2HttpTafResp(access,null,rp.error,RESP.FAIL,resp,true);
+                               }
+                       } catch (APIException | CadiException | LocatorException e) {
+                               return new OAuth2HttpTafResp(access,null,"Bearer Token invalid",RESP.FAIL,resp,true);
+                       } catch (NoSuchAlgorithmException e) {
+                               return new OAuth2HttpTafResp(access,null,"Security Algorithm not available",RESP.FAIL,resp,true);
+                       }
+               }
+               return new OAuth2HttpTafResp(access,null,"No OAuth2 ",RESP.TRY_ANOTHER_TAF,resp,true);
+       }
+
+       @Override
+       public Resp revalidate(CachedPrincipal prin,Object state) {
+               //TODO!!!!
+               return null;
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java
new file mode 100644 (file)
index 0000000..7e1028a
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TrustPrincipal;
+import org.onap.aaf.cadi.taf.AbsTafResp;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class OAuth2HttpTafResp extends AbsTafResp implements TafResp {
+       private HttpServletResponse httpResp;
+       private RESP status;
+       private final boolean wasFailed;
+       
+       public OAuth2HttpTafResp(Access access, OAuth2Principal principal, String desc, RESP status, HttpServletResponse resp, boolean wasFailed) {
+               super(access,principal, desc);
+               httpResp = resp;
+               this.status = status; 
+               this.wasFailed = wasFailed;
+       }
+
+       public OAuth2HttpTafResp(Access access, TrustPrincipal principal, String desc, RESP status,HttpServletResponse resp) {
+               super(access,principal, desc);
+               httpResp = resp;
+               this.status = status; 
+               wasFailed = true; // if Trust Principal added, must be good
+       }
+
+       public RESP authenticate() throws IOException {
+               httpResp.setStatus(401); // Unauthorized        
+               return RESP.HTTP_REDIRECT_INVOKED;
+       }
+
+       public RESP isAuthenticated() {
+               return status;
+       }
+
+       public boolean isFailedAttempt() {
+               return wasFailed;
+       }
+
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java
new file mode 100644 (file)
index 0000000..89816a2
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.lur.LocalPermission;
+import org.onap.aaf.cadi.principal.BearerPrincipal;
+import org.onap.aaf.misc.env.util.Split;
+
+public class OAuth2Lur implements Lur {
+       private TokenMgr tm;
+
+       public OAuth2Lur(TokenMgr tm) {
+               this.tm = tm;
+       }
+       
+       @Override
+       public Permission createPerm(String p) {
+               String[] params = Split.split('|', p);
+               if(params.length==3) {
+                       return new AAFPermission(params[0],params[1],params[2]);
+               } else {
+                       return new LocalPermission(p);
+               }
+       }
+
+       @Override
+       public boolean fish(Principal bait, Permission pond) {
+               AAFPermission apond = (AAFPermission)pond;
+               OAuth2Principal oap;
+               if(bait instanceof OAuth2Principal) {
+                       oap = (OAuth2Principal)bait; 
+               } else {
+                       // Here is the spot to put in Principal Conversions
+                       return false;
+               }
+
+               TokenPerm tp = oap.tokenPerm();
+               if(tp==null) {
+               } else {
+                       for(Permission p : tp.perms()) {
+                               if(p.match(apond)) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       @Override
+       public void fishAll(Principal bait, List<Permission> permissions) {
+               OAuth2Principal oap = (OAuth2Principal)bait;
+               TokenPerm tp = oap.tokenPerm();
+               if(tp!=null) {
+                       for(AAFPermission p : tp.perms()) {
+                               permissions.add(p);
+                       }
+               }
+       }
+
+       @Override
+       public void destroy() {
+       }
+
+       @Override
+       public boolean handlesExclusively(Permission pond) {
+               return false;
+       }
+
+       @Override
+       public boolean handles(Principal p) {
+               if(p!=null && p instanceof BearerPrincipal) {
+                       return ((BearerPrincipal)p).getBearer()!=null;
+               }
+               return false;
+       }
+
+       @Override
+       public void clear(Principal p, StringBuilder report) {
+               tm.clear(p,report);
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java
new file mode 100644 (file)
index 0000000..90d5963
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+public class OAuth2Principal extends TaggedPrincipal {
+       private TokenPerm tp;
+//     private byte[] hash; // hashed cred for disk validation
+       
+       public OAuth2Principal(TokenPerm tp, byte[] hash) {
+               this.tp = tp;
+//             this.hash = hash;
+       }
+       
+       @Override
+       public String getName() {
+               return tp.getUsername();
+       }
+       
+       public TokenPerm tokenPerm() {
+               return tp;
+       }
+
+       @Override
+       public String tag() {
+               return "OAuth";
+       }
+
+       @Override
+       public String personalName() {
+               return tp.getUsername();
+       }
+       
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java
new file mode 100644 (file)
index 0000000..d4f343f
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.nio.file.Path;
+
+import org.onap.aaf.cadi.persist.Persist;
+import org.onap.aaf.cadi.persist.Persistable;
+import org.onap.aaf.cadi.persist.Persisting;
+
+import aafoauth.v2_0.Token;
+
+/**
+ * TimedToken
+ *   Tokens come from the Token Server with an "Expired In" setting.  This class will take that, and
+ *   create a date from time of Creation, which works with local code.
+ *   
+ * We create a Derived class, so that it can be used as is the originating Token type.
+ * 
+ * "expired" is local computer time 
+ * @author Jonathan
+ *
+ */
+// Package on purpose
+public class TimedToken extends Token implements Persistable<Token> {
+       private Persisting<Token> cacheable; // no double inheritance... 
+
+//     public TimedToken(Token t, byte[] hash) {
+//             this(t,(System.currentTimeMillis()/1000)+t.getExpiresIn(),hash,null);
+//     }
+//
+       public TimedToken(Persist<Token,?> p, Token t, byte[] hash, Path path){
+               this(p,t,t.getExpiresIn()+(System.currentTimeMillis()/1000),hash, path);
+       }
+       
+       public TimedToken(Persist<Token,?> p, Token t, long expires_secsFrom1970, byte[] hash, Path path) {
+               cacheable = new Persisting<Token>(p, t,expires_secsFrom1970, hash, path);
+               accessToken=t.getAccessToken();
+               expiresIn=t.getExpiresIn();
+               refreshToken=t.getRefreshToken();
+               scope = t.getScope();
+               state = t.getState();
+               tokenType = t.getTokenType();
+       }
+
+
+       @Override
+       public Token get() {
+               return cacheable.get();
+       }
+
+       @Override
+       public boolean checkSyncTime() {
+               return cacheable.checkSyncTime();
+       }
+
+       @Override
+       public boolean checkReloadable() {
+               return cacheable.checkReloadable();
+       }
+
+       @Override
+       public boolean hasBeenTouched() {
+               return cacheable.hasBeenTouched();
+       }
+
+       @Override
+       public long expires() {
+               return cacheable.expires();
+       }
+
+       @Override
+       public boolean expired() {
+               return cacheable.expired();
+       }
+
+       @Override
+       public boolean match(byte[] hashIn) {
+               return cacheable.match(hashIn);
+       }
+
+       @Override
+       public byte[] getHash() {
+               return cacheable.getHash();
+       }
+
+       @Override
+       public void inc() {
+               cacheable.inc();
+       }
+
+       @Override
+       public int count() {
+               return cacheable.count();
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.oauth.Persistable#clearCount()
+        */
+       @Override
+       public void clearCount() {
+               cacheable.clearCount();
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.persist.Persistable#path()
+        */
+       @Override
+       public Path path() {
+               return cacheable.path();
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java
new file mode 100644 (file)
index 0000000..4b0c944
--- /dev/null
@@ -0,0 +1,474 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.URLEncoder;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon.GetSetter;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.persist.Persist.Loader;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+import aafoauth.v2_0.Introspect;
+import aafoauth.v2_0.Token;
+
+public class TokenClient {
+       private static final String UTF_8 = "UTF-8";
+
+       public enum AUTHN_METHOD {client_credentials,password,payload,basic_auth,certificate,refresh_token, none}
+
+       private final TokenClientFactory factory;
+       private final AAFCon<?> tkCon;
+       private static RosettaDF<Token> tokenDF;
+       protected static RosettaDF<Introspect> introspectDF;
+
+
+       private int timeout;
+       private String client_id, username;
+       private byte[] enc_client_secret, enc_password;
+
+       private GetSetter ss;
+       private AUTHN_METHOD authn_method;
+       private byte[] hash;
+       private final char okind;
+       private String default_scope;
+
+       // Package on Purpose
+       TokenClient(char okind, final TokenClientFactory tcf, final AAFCon<?> tkCon, final int timeout, AUTHN_METHOD am) throws CadiException, APIException {
+               this.okind = okind;
+               factory = tcf;
+               this.tkCon = tkCon;
+               this.timeout = timeout;
+               ss = null;
+               authn_method = am;
+               synchronized(tcf) {
+                       if(introspectDF==null) {
+                               tokenDF = tkCon.env().newDataFactory(Token.class);
+                               introspectDF = tkCon.env().newDataFactory(Introspect.class);
+                       }
+               }
+       }
+
+       public void client_id(String client_id) {
+               this.client_id = client_id;
+               default_scope = FQI.reverseDomain(client_id);
+       }
+       
+       public String client_id() {
+               return client_id;
+       }
+       
+       /**
+        * This scope based on client_id... the App configured for call
+        * @return
+        */
+       public String defaultScope() {
+               return default_scope;
+       }
+
+       public void client_creds(Access access) throws CadiException {
+               if(okind=='A') {
+                       client_creds(access.getProperty(Config.AAF_APPID, null),access.getProperty(Config.AAF_APPPASS, null));
+               } else {
+                       client_creds(access.getProperty(Config.AAF_ALT_CLIENT_ID, null),access.getProperty(Config.AAF_ALT_CLIENT_SECRET, null));
+               }
+       }
+
+       /**
+        * Note: OAuth2 provides for normal Authentication parameters when getting tokens.  Basic Auth is one such valid
+        * way to get Credentials.  However, support is up to the OAuth2 Implementation
+        * 
+        * This method is for setting an App's creds (client) to another App.
+        * 
+        * @param client_id
+        * @param client_secret
+        * @throws IOException
+        */
+       public void client_creds(final String client_id, final String client_secret) throws CadiException {
+               if(client_id==null) {
+                       throw new CadiException(Config.AAF_ALT_CLIENT_ID + " is null");
+               }
+               this.client_id = client_id;
+               default_scope = FQI.reverseDomain(client_id);
+
+               if(client_secret!=null) {
+                       try {
+                               if(client_secret.startsWith("enc:")) {
+                                       final String temp = factory.access.decrypt(client_secret, false); // this is a more powerful, but non-thread-safe encryption
+                                       hash = Hash.hashSHA256(temp.getBytes());
+                                       this.enc_client_secret = factory.symm.encode(temp.getBytes());
+                                       ss = new GetSetter() {
+                                               @Override
+                                               public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException {
+                                                       return con.basicAuth(client_id, temp);// Base class encrypts password
+                                               }
+                                       };
+                               } else {
+                                       byte[] temp = client_secret.getBytes();
+                                       hash = Hash.hashSHA256(temp);
+                                       this.enc_client_secret = factory.symm.encode(temp);
+                                       ss = new GetSetter() {
+                                               @Override
+                                               public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException {
+                                                       return con.basicAuth(client_id, client_secret);// Base class encrypts password
+                                               }
+                                       };
+                               }
+                               authn_method = AUTHN_METHOD.client_credentials;
+                       } catch(IOException | NoSuchAlgorithmException e) {
+                               throw new CadiException(e);
+                       }
+               }
+       }
+       
+       public void username(String username) {
+               this.username = username;
+       }
+
+       /**
+        * Note: OAuth2 provides for normal Authentication parameters when getting tokens.  Basic Auth is one such valid
+        * way to get Credentials.  However, support is up to the OAuth2 Implementation
+        * 
+        * This method is for setting the End-User's Creds
+        * 
+        * @param client_id
+        * @param client_secret
+        * @throws IOException
+        */
+       public void password(final String user, final String password) throws CadiException {
+               this.username = user;
+               if(password!=null) {
+                       try {
+                               if(password.startsWith("enc:")) {
+                                       final String temp = factory.access.decrypt(password, false); // this is a more powerful, but non-thread-safe encryption
+                                       hash = Hash.hashSHA256(temp.getBytes());
+                                       this.enc_password = factory.symm.encode(temp.getBytes());
+                                       ss = new GetSetter() {
+                                               @Override
+                                               public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException {
+                                                       return con.basicAuth(user, temp);// Base class encrypts password
+                                               }
+                                       };
+                               } else {
+                                       byte[] temp = password.getBytes();
+                                       hash = Hash.hashSHA256(temp);
+                                       this.enc_password = factory.symm.encode(temp);
+                                       ss = new GetSetter() {
+                                               @Override
+                                               public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException {
+                                                       return con.basicAuth(user, password);// Base class encrypts password
+                                               }
+                                       };
+                               }
+                               authn_method = AUTHN_METHOD.password;
+                       } catch (IOException | NoSuchAlgorithmException e) {
+                               throw new CadiException(e);
+                       }
+               }
+       }
+       
+       public void clearEndUser() {
+               username = null;
+               enc_password = null;
+               if(client_id!=null && enc_client_secret!=null) {
+                       authn_method = AUTHN_METHOD.client_credentials;
+               } else {
+                       authn_method = AUTHN_METHOD.password;
+               }
+       }
+
+       public Result<TimedToken> getToken(final String ... scopes) throws LocatorException, CadiException, APIException {
+               return getToken(Kind.OAUTH,scopes);
+       }
+
+       public void clearToken(final String ... scopes) throws CadiException {
+               clearToken(Kind.OAUTH,scopes);
+       }
+
+       public void clearToken(final char kind, final String ... scopes) throws CadiException {
+               final String scope = addScope(scopes);
+               char c;
+               if(kind==Kind.OAUTH) {
+                       c = okind;
+               } else {
+                       c = kind;
+               }
+               final String key = TokenClientFactory.getKey(c,client_id,username,hash,scope);
+               factory.delete(key);
+       }
+       /**
+        * Get AuthToken
+        * @throws APIException 
+        * @throws CadiException 
+        * @throws LocatorException 
+        */
+       public Result<TimedToken> getToken(final char kind, final String ... scopes) throws LocatorException, CadiException, APIException {
+               final String scope = addScope(scopes);
+               char c;
+               if(kind==Kind.OAUTH) {
+                       c = okind;
+               } else {
+                       c = kind;
+               }
+               final String key = TokenClientFactory.getKey(c,client_id,username,hash,scope);
+               if(ss==null) {
+                       throw new APIException("client_creds(...) must be set before obtaining Access Tokens");
+               }
+               
+               Result<TimedToken> rtt = factory.get(key,hash,new Loader<TimedToken>() {
+                       @Override
+                       public Result<TimedToken> load(final String key) throws APIException, CadiException, LocatorException {
+                               final List<String> params = new ArrayList<String>();
+                               params.add(scope);
+                               addSecurity(params,authn_method);
+                       
+                               final String paramsa[] = new String[params.size()];
+                               params.toArray(paramsa);
+                               Result<Token> rt = tkCon.best(new Retryable<Result<Token>>() {
+                                       @Override
+                                       public Result<Token> code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                                               // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test
+                                               Future<Token> f = client.postForm(null,tokenDF,paramsa);
+                                               if(f.get(timeout)) {
+                                                       return Result.ok(f.code(),f.value);
+                                               } else {
+                                                       return Result.err(f.code(), f.body());
+                                               }
+                                       }
+                               });
+                               
+                               if(rt.isOK()) {
+                                       try {
+                                               return Result.ok(rt.code,factory.putTimedToken(key,rt.value, hash));
+                                       } catch (IOException e) {
+                                               // TODO What to do here?
+                                               e.printStackTrace();
+                                               return Result.err(999,e.getMessage());
+                                       }
+                               } else {
+                                       return Result.err(rt);
+                               }
+                       }
+               });
+               if(rtt.isOK()) { // not validated for Expired
+                       TimedToken tt = rtt.value;
+                       if(tt.expired()) {
+                               rtt = refreshToken(tt);
+                               if(rtt.isOK()) {
+                                       tkCon.access.printf(Level.INFO, "Refreshed token %s to %s",tt.getAccessToken(),rtt.value.getAccessToken());
+                                       return Result.ok(200,rtt.value);
+                               } else {
+                                       tkCon.access.printf(Level.INFO, "Expired token %s cannot be renewed %d %s",tt.getAccessToken(),rtt.code,rtt.error);
+                                       factory.delete(key);
+                                       tt=null;
+                               }
+                       } else {
+                               return Result.ok(200,tt);
+                       }
+               } else {
+                       Result.err(rtt);
+               }
+               return Result.err(404,"Not Found");
+       }
+       
+       public Result<TimedToken> refreshToken(Token token) throws APIException, LocatorException, CadiException {
+               if(ss==null) {
+                       throw new APIException("client_creds(...) must be set before obtaining Access Tokens");
+               }
+               final List<String> params = new ArrayList<String>();
+               params.add("refresh_token="+token.getRefreshToken());
+               addSecurity(params,AUTHN_METHOD.refresh_token);
+               final String scope="scope="+token.getScope().replace(' ', '+');
+               params.add(scope);
+       
+               final String paramsa[] = new String[params.size()];
+               params.toArray(paramsa);
+               Result<Token> rt = tkCon.best(new Retryable<Result<Token>>() {
+                       @Override
+                       public Result<Token> code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                               // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test
+                               Future<Token> f = client.postForm(null,tokenDF,paramsa);
+                               if(f.get(timeout)) {
+                                       return Result.ok(f.code(),f.value);
+                               } else {
+                                       return Result.err(f.code(), f.body());
+                               }
+                       }
+               });
+               String key =  TokenClientFactory.getKey(okind,client_id, username, hash, scope);
+               if(rt.isOK()) {
+                       try {
+                               return Result.ok(200,factory.putTimedToken(key, rt.value, hash));
+                       } catch (IOException e) {
+                               //TODO what to do here?
+                               return Result.err(999, e.getMessage());
+                       }
+               } else if(rt.code==404) {
+                       factory.deleteFromDisk(key);
+               }
+               return Result.err(rt);
+       }
+
+       public Result<Introspect> introspect(final String token) throws APIException, LocatorException, CadiException {
+               if(ss==null) {
+                       throw new APIException("client_creds(...) must be set before introspecting Access Tokens");
+               }
+
+               return tkCon.best(new Retryable<Result<Introspect>>() {
+                               @Override
+                               public Result<Introspect> code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                                       final List<String> params = new ArrayList<String>();
+                                       params.add("token="+token);
+                                       addSecurity(params,AUTHN_METHOD.client_credentials);
+                                       final String paramsa[] = new String[params.size()];
+                                       params.toArray(paramsa);
+                                       // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test
+                                       Future<Introspect> f = client.postForm(null,introspectDF,paramsa);
+                                       if(f.get(timeout)) {
+                                               return Result.ok(f.code(),f.value);
+                                       } else {
+                                               return Result.err(f.code(), f.body());
+                                       }
+                               }
+                       }
+               );
+       }
+       
+       private String addScope(String[] scopes) {
+               String rv = null;
+               StringBuilder scope=null;
+               boolean first = true;
+               for(String s : scopes) {
+                       if(first) {
+                               scope = new StringBuilder();
+                               scope.append("scope=");
+                               first=false;
+                       } else {
+                               scope.append('+');
+                       }
+                       scope.append(s);
+               }
+               if(scope!=null) {
+                       rv=scope.toString();
+               }
+               return rv;
+       }
+
+       private void addSecurity(List<String> params, AUTHN_METHOD authn) throws APIException {
+               // Set GrantType... different than Credentials
+               switch(authn) {
+                       case client_credentials:
+                               params.add("grant_type=client_credentials");
+                               break;
+                       case password:
+                               params.add("grant_type=password");
+                               break;
+                       case refresh_token:
+                               params.add("grant_type=refresh_token");
+                               break;
+                       case none:
+                               break;
+                       default:
+                               // Nothing to do
+                               break;
+               }
+               
+               // Set Credentials appropriate 
+               switch(authn_method) {
+                       case client_credentials:
+                               if(client_id!=null) {
+                                       params.add("client_id="+client_id);
+                               }
+               
+                               if(enc_client_secret!=null) {
+                                       try {
+                                               params.add("client_secret="+URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8));
+                                       } catch (IOException e) {
+                                               throw new APIException("Error Decrypting Password",e);
+                                       }
+                               }
+                               break;
+                       case refresh_token:
+                               if(client_id!=null) {
+                                       params.add("client_id="+client_id);
+                               }
+               
+                               if(enc_client_secret!=null) {
+                                       try {
+                                               params.add("client_secret="+URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8));
+                                       } catch (IOException e) {
+                                               throw new APIException("Error Decrypting Password",e);
+                                       }
+                               }
+                               break;
+
+                       case password:
+                               if(client_id!=null) {
+                                       params.add("client_id="+client_id);
+                               }
+               
+                               if(enc_client_secret!=null) {
+                                       try {
+                                               params.add("client_secret="+ URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8));
+                                       } catch (IOException e) {
+                                               throw new APIException("Error Decrypting Password",e);
+                                       }
+                               }
+                               if(username!=null) {
+                                       params.add("username="+username);
+                               }
+               
+                               if(enc_password!=null) {
+                                       try {
+                                               params.add("password="+ URLEncoder.encode(new String(factory.symm.decode(enc_password)),UTF_8));
+                                       } catch (IOException e) {
+                                               throw new APIException("Error Decrypting Password",e);
+                                       }
+                               }
+       
+                               break;
+                       default:
+                               // Nothing to do
+                               break;
+               }
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java
new file mode 100644 (file)
index 0000000..3f6fa59
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+import org.onap.aaf.cadi.oauth.TokenClient.AUTHN_METHOD;
+import org.onap.aaf.cadi.persist.Persist;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aafoauth.v2_0.Token;
+
+public class TokenClientFactory extends Persist<Token,TimedToken> {
+       private static TokenClientFactory instance;
+       private Map<String,AAFConHttp> aafcons = new ConcurrentHashMap<String, AAFConHttp>();
+       private SecurityInfoC<HttpURLConnection> hsi;
+       // Package on purpose
+       final Symm symm;        
+
+       private TokenClientFactory(Access pa) throws APIException, GeneralSecurityException, IOException, CadiException {
+               super(pa, new RosettaEnv(pa.getProperties()),Token.class,"outgoing");
+               symm = Symm.encrypt.obtain();
+               hsi = SecurityInfoC.instance(access, HttpURLConnection.class);
+       }
+       
+       public synchronized static final TokenClientFactory instance(Access access) throws APIException, GeneralSecurityException, IOException, CadiException {
+               if(instance==null) {
+                       instance = new TokenClientFactory(access);
+               }
+               return instance;
+       }
+
+       /**
+        * Pickup Timeout from Properties
+        * 
+        * @param tagOrURL
+        * @return
+        * @throws CadiException
+        * @throws LocatorException
+        * @throws APIException
+        */
+       public<INTR> TokenClient newClient(final String tagOrURL) throws CadiException, LocatorException, APIException {
+               return newClient(tagOrURL,Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF)));
+       }
+       
+       public<INTR> TokenClient newClient(final String tagOrURL, final int timeout) throws CadiException, LocatorException, APIException {
+               AAFConHttp ach;
+               if(tagOrURL==null) {
+                       throw new CadiException("parameter tagOrURL cannot be null.");
+               } else {
+                       ach = aafcons.get(tagOrURL);
+                       if(ach==null) {
+                               aafcons.put(tagOrURL, ach=new AAFConHttp(access,tagOrURL));
+                       }
+               }
+               char okind;
+               if(Config.AAF_OAUTH2_TOKEN_URL.equals(tagOrURL) || 
+                       tagOrURL.equals(access.getProperty(Config.AAF_OAUTH2_TOKEN_URL, null))) {
+                               okind = Kind.AAF_OAUTH;
+                       } else {
+                               okind = Kind.OAUTH;
+                       }
+               return new TokenClient(
+                               okind,
+                               this,
+                               ach,
+                               timeout,
+                               AUTHN_METHOD.none);
+       }
+       
+       public TzClient newTzClient(final String locatorURL) throws CadiException, LocatorException {
+               try {
+                       return new TzHClient(access,hsi,bestLocator(locatorURL));
+               } catch (URISyntaxException e) {
+                       throw new LocatorException(e);
+               }
+       }
+
+       static String getKey(char tokenSource,String client_id, String username, byte[] hash, String scope) throws CadiException {
+               try {
+                       StringBuilder sb = new StringBuilder(client_id);
+                       sb.append('_');
+                       if(username!=null) {
+                               sb.append(username);
+                       }
+                       sb.append('_');
+                       sb.append(tokenSource);
+                       byte[] tohash=scope.getBytes();
+                       if(hash!=null && hash.length>0) {
+                               byte temp[] = new byte[hash.length+tohash.length];
+                               System.arraycopy(tohash, 0, temp, 0, tohash.length);
+                               System.arraycopy(hash, 0, temp, tohash.length, hash.length);
+                               tohash = temp;
+                       }
+                       if(scope!=null && scope.length()>0) {
+                               sb.append(Hash.toHexNo0x(Hash.hashSHA256(tohash)));
+                       }
+                       return sb.toString();
+               } catch (NoSuchAlgorithmException e) {
+                       throw new CadiException(e);
+               }
+       }
+
+       @Override
+       protected TimedToken newCacheable(Token t, long expires, byte[] hash, Path path) throws IOException {
+               return new TimedToken(this,t,expires,hash,path);
+       }
+
+       public TimedToken putTimedToken(String key, Token token, byte[] hash) throws IOException, CadiException {
+               TimedToken tt = new TimedToken(this,token,token.getExpiresIn()+(System.currentTimeMillis()/1000),hash,getPath(key));
+               put(key,tt);
+               return tt;
+       }
+       
+       private static final Pattern locatePattern = Pattern.compile("https://.*/locate/.*");
+       public Locator<URI> bestLocator(final String locatorURL ) throws LocatorException, URISyntaxException {
+               if(locatorURL==null) {
+                       throw new LocatorException("Cannot have a null locatorURL in bestLocator");
+               }
+               if(locatePattern.matcher(locatorURL).matches()) {
+                       return new AAFLocator(hsi,new URI(locatorURL));
+               } else if(locatorURL.contains("//DME2RESOLVE/")) {
+                       throw new LocatorException("DME2Locator doesn't exist.  Use DME2 specific Clients");
+               } else {
+                       return new PropertyLocator(locatorURL);
+               }
+               // Note: Removed DME2Locator... If DME2 client is needed, use DME2Clients
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java
new file mode 100644 (file)
index 0000000..d8fd88f
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.persist.Persist;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Perms;
+import aafoauth.v2_0.Introspect;
+
+public class TokenMgr extends Persist<Introspect, TokenPerm> {
+       protected static Map<String,TokenPerm> tpmap = new ConcurrentHashMap<String, TokenPerm>();
+       protected static Map<String,TokenMgr> tmmap = new HashMap<String, TokenMgr>(); // synchronized in getInstance
+       protected static Map<String,String> currentToken = new HashMap<String,String>(); // synchronized in getTP
+       public static RosettaDF<Perms> permsDF;
+       public static RosettaDF<Introspect> introspectDF;
+
+       private final TokenPermLoader tpLoader;
+       
+       private TokenMgr(PropAccess access, String tokenURL, String introspectURL) throws APIException, CadiException {
+               super(access,new RosettaEnv(access.getProperties()),Introspect.class,"introspect");
+               synchronized(access) {
+                       if(permsDF==null) {
+                               permsDF = env.newDataFactory(Perms.class);
+                               introspectDF = env.newDataFactory(Introspect.class);
+                       }
+               }
+               if("dbToken".equals(tokenURL) && "dbIntrospect".equals(introspectURL)) {
+                       tpLoader = new TokenPermLoader() { // null Loader
+                               @Override
+                               public Result<TokenPerm> load(String accessToken, byte[] cred)
+                                               throws APIException, CadiException, LocatorException {
+                                       return Result.err(404, "DBLoader");
+                               }
+                       };
+               } else {
+                       RemoteTokenPermLoader rtpl = new RemoteTokenPermLoader(tokenURL, introspectURL); // default is remote
+                       String i = access.getProperty(Config.AAF_APPID,null);
+                       String p = access.getProperty(Config.AAF_APPPASS, null);
+                       if(i==null || p==null) {
+                               throw new CadiException(Config.AAF_APPID + " and " + Config.AAF_APPPASS + " must be set to initialize TokenMgr");
+                       }
+                       rtpl.introCL.client_creds(i,p);
+                       tpLoader = rtpl;
+               }
+       }
+
+       private TokenMgr(PropAccess access, TokenPermLoader tpl) throws APIException, CadiException {
+               super(access,new RosettaEnv(access.getProperties()),Introspect.class,"incoming");
+               synchronized(access) {
+                       if(permsDF==null) {
+                               permsDF = env.newDataFactory(Perms.class);
+                               introspectDF = env.newDataFactory(Introspect.class);
+                       }
+               }
+               tpLoader = tpl;
+       }
+
+       public static synchronized TokenMgr getInstance(final PropAccess access, final String tokenURL, final String introspectURL) throws APIException, CadiException {
+               String key;
+               TokenMgr tm = tmmap.get(key=tokenURL+'/'+introspectURL);
+               if(tm==null) {
+                       tmmap.put(key, tm=new TokenMgr(access,tokenURL,introspectURL));
+               }
+               return tm;
+       }
+       
+       public Result<OAuth2Principal> toPrincipal(final String accessToken, final byte[] hash) throws APIException, CadiException, LocatorException {
+               Result<TokenPerm> tp = get(accessToken, hash, new Loader<TokenPerm>() {
+                       @Override
+                       public Result<TokenPerm> load(String key) throws APIException, CadiException, LocatorException {
+                               try {
+                                       return tpLoader.load(accessToken,hash);
+                               } catch (APIException | LocatorException e) {
+                                       throw new CadiException(e);
+                               }
+                       }
+               });
+               if(tp.isOK()) {
+                       return Result.ok(200, new OAuth2Principal(tp.value,hash));
+               } else {
+                       return Result.err(tp);
+               }
+       }
+       
+       public Result<TokenPerm> get(final String accessToken, final byte[] hash) throws APIException, CadiException, LocatorException {
+               return get(accessToken,hash,new Loader<TokenPerm>() {
+                       @Override
+                       public Result<TokenPerm> load(String key) throws APIException, CadiException, LocatorException {
+                               return tpLoader.load(key,hash);
+                       }
+                       
+               });
+//             return tpLoader.load(accessToken,hash);
+       }
+
+       public interface TokenPermLoader{
+               public Result<TokenPerm> load(final String accessToken, final byte[] cred) throws APIException, CadiException, LocatorException;
+       }
+       
+       private class RemoteTokenPermLoader implements TokenPermLoader {
+               private TokenClientFactory tcf;
+               private TokenClient tokenCL, introCL;
+
+               public RemoteTokenPermLoader(final String tokenURL, final String introspectURL) throws APIException, CadiException {
+                       try {
+                               tcf = TokenClientFactory.instance(access);
+                               int timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
+                               tokenCL = tcf.newClient(tokenURL, 
+                                                                               timeout);
+                               if(introspectURL.equals(tokenURL)) {
+                                       introCL = tokenCL;
+                               } else {
+                                       introCL = tcf.newClient(introspectURL, 
+                                                       timeout);
+                               }
+
+                       } catch (GeneralSecurityException | IOException | NumberFormatException | LocatorException e) {
+                               throw new CadiException(e);
+                       }
+               }
+                
+               public Result<TokenPerm> load(final String accessToken, final byte[] cred) throws APIException, CadiException, LocatorException {
+                       long start = System.currentTimeMillis();
+                       try {
+                               Result<Introspect> ri = introCL.introspect(accessToken);
+                               if(ri.isOK()) {
+                                       return Result.ok(ri.code, new TokenPerm(TokenMgr.this,permsDF,ri.value,cred,getPath(accessToken)));
+                               } else {
+                                       return Result.err(ri);
+                               }
+                       } finally {
+                               access.printf(Level.INFO, "Token loaded in %d ms",System.currentTimeMillis()-start);
+                       }
+               }
+       }
+
+       public void clear(Principal p, StringBuilder report) {
+               TokenPerm tp = tpmap.remove(p.getName());
+               if(tp==null) {
+                       report.append("Nothing to clear");
+               } else {
+                       report.append("Cleared ");
+                       report.append(p.getName());
+               }
+       }
+
+       @Override
+       protected TokenPerm newCacheable(Introspect i, long expires, byte[] hash, Path path) throws APIException {
+               // Note: Introspect drives the Expiration... ignoring expires.
+               return new TokenPerm(this,permsDF,i,hash,path);
+       }
+
+       public TokenPerm putIntrospect(Introspect intro, byte[] cred) throws APIException {
+               return newCacheable(intro, intro.getExp(), cred, getPath(intro.getAccessToken()));
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java
new file mode 100644 (file)
index 0000000..4a0259a
--- /dev/null
@@ -0,0 +1,171 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.persist.Persist;
+import org.onap.aaf.cadi.persist.Persisting;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.rosetta.InJson;
+import org.onap.aaf.misc.rosetta.Parse;
+import org.onap.aaf.misc.rosetta.ParseException;
+import org.onap.aaf.misc.rosetta.Parsed;
+import org.onap.aaf.misc.rosetta.InJson.State;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+import aaf.v2_0.Perms;
+import aafoauth.v2_0.Introspect;
+
+public class TokenPerm extends Persisting<Introspect>{
+       private static final List<AAFPermission> NULL_PERMS = new ArrayList<AAFPermission>();
+       private Introspect introspect;
+       private List<AAFPermission> perms;
+       private String scopes;
+       public TokenPerm(Persist<Introspect,?> p, RosettaDF<Perms> permsDF, Introspect ti, byte[] hash, Path path) throws APIException {
+               super(p,ti,ti.getExp(),hash,path); // ti.getExp() is seconds after Jan 1, 1970 )
+               this.introspect = ti;
+               if(ti.getContent()==null || ti.getContent().length()==0) {
+                       perms = NULL_PERMS;
+               } else {
+                       LoadPermissions lp;
+                       try {
+                               lp = new LoadPermissions(new StringReader(ti.getContent()));
+                               perms = lp.perms;
+                       } catch (ParseException e) {
+                               throw new APIException("Error parsing Content",e);
+                       }
+               }
+               scopes = ti.getScope();
+       }
+       
+       public List<AAFPermission> perms() {
+               return perms;
+       }
+       
+       public String getClientId() {
+               return introspect.getClientId();
+       }
+       
+       public String getUsername() {
+               return introspect.getUsername();
+       }
+       
+       public String getToken() {
+               return introspect.getAccessToken();
+       }
+       
+       public synchronized String getScopes() {
+               return scopes;
+       }
+
+       public Introspect getIntrospect() {
+               return introspect;
+       }
+       
+       // Direct Parse Perms into List
+       public static class LoadPermissions {
+               public List<AAFPermission> perms;
+
+               public LoadPermissions(Reader r) throws ParseException {
+                       PermInfo pi = new PermInfo();
+                       InJson ij = new InJson();
+                       Parsed<State> pd =  ij.newParsed();
+                       boolean inPerms = false, inPerm = false;
+                       while((pd = ij.parse(r,pd.reuse())).valid()) {
+                               switch(pd.event) {
+                                       case Parse.START_DOC:
+                                               perms = new ArrayList<AAFPermission>();
+                                               break;
+                                       case Parse.START_ARRAY:
+                                               inPerms = "perm".equals(pd.name);
+                                               break;
+                                       case '{':
+                                               if(inPerms) {
+                                                       inPerm=true;
+                                                       pi.clear();
+                                               }
+                                               break;
+                                       case ',':
+                                               if(inPerm) {
+                                                       pi.eval(pd);
+                                               }
+                                               break;
+                                       case '}':
+                                               if(inPerms) {
+                                                       if(inPerm) {
+                                                               pi.eval(pd);
+                                                               AAFPermission perm = pi.create();
+                                                               if(perm!=null) {
+                                                                       perms.add(perm);
+                                                               }
+                                                       }
+                                                       inPerm=false;
+                                               }
+                                               break;
+                                       case Parse.END_ARRAY:
+                                               if(inPerms) {
+                                                       inPerms=false;
+                                               }
+                                               break;
+                                       case Parse.END_DOC:
+                                               break;
+                               }
+                       }
+               }
+       }
+       
+       // Gathering object for parsing objects, then creating AAF Permission
+       private static class PermInfo {
+               public String type,instance,action;
+               public void clear() {
+                       type=instance=action=null;
+               }
+               public void eval(Parsed<State> pd) {
+                       if(pd.hasName()) {
+                               switch(pd.name) {
+                                       case "type":
+                                               type=pd.sb.toString();
+                                               break;
+                                       case "instance":
+                                               instance=pd.sb.toString();
+                                               break;
+                                       case "action":
+                                               action=pd.sb.toString();
+                                               break;
+                               }
+                       }
+               }
+               public AAFPermission create() {
+                       if(type!=null && instance!=null && action !=null) {
+                               return new AAFPermission(type, instance, action);
+                       } else {
+                               return null;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java
new file mode 100644 (file)
index 0000000..a14c0f8
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.misc.env.APIException;
+
+/**
+ * TimedToken Client
+ * 
+ * @author Jonathan
+ *
+ */
+public abstract class TzClient {
+       public abstract void setToken(final String client_id, final TimedToken token) throws IOException;
+       public abstract <RET> RET best(Retryable<RET> rcode) throws CadiException, LocatorException, APIException;
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java
new file mode 100644 (file)
index 0000000..c565fa8
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HMangr;
+import org.onap.aaf.cadi.http.HTokenSS;
+import org.onap.aaf.misc.env.APIException;
+
+/**
+ * Tokenized HClient
+ * 
+ * @author Jonathan
+ *
+ */
+public class TzHClient extends TzClient {
+       private HMangr hman;
+       public SecurityInfoC<HttpURLConnection> si;
+       private TimedToken token;
+       private SecuritySetter<HttpURLConnection> tokenSS;
+
+       public TzHClient(Access access, String tagOrURL) throws CadiException, LocatorException {
+               try {
+                       si = SecurityInfoC.instance(access, HttpURLConnection.class);
+                       hman = new HMangr(access, new AAFLocator(si,new URI(access.getProperty(tagOrURL, tagOrURL))));
+               } catch (URISyntaxException e) {
+                       throw new CadiException(e);
+               }
+       }
+       public TzHClient(Access access, SecurityInfoC<HttpURLConnection> hsi, Locator<URI> loc) throws LocatorException {
+               si = hsi;
+               hman = new HMangr(access, loc);
+       }
+       
+       public void setToken(final String client_id, TimedToken token) throws IOException {
+               this.token = token;
+               tokenSS = new HTokenSS(si, client_id, token.getAccessToken());
+       }
+
+       public <RET> RET best (Retryable<RET> retryable) throws CadiException, LocatorException, APIException {
+               if(token == null || tokenSS==null) {
+                       throw new CadiException("OAuth2 Token has not been set");
+               }
+               if(token.expired()) {
+                       //TODO Refresh?
+                       throw new CadiException("Expired Token");
+               } else {
+                       return hman.best(tokenSS, retryable);
+               }
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java
new file mode 100644 (file)
index 0000000..ff0c246
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.obasic;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Taf;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.CredVal.Type;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.oauth.AbsOTafLur;
+import org.onap.aaf.cadi.oauth.OAuth2Principal;
+import org.onap.aaf.cadi.oauth.TimedToken;
+import org.onap.aaf.cadi.oauth.TokenClient;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+
+/**
+ * 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 OBasicHttpTaf extends AbsOTafLur implements HttpTaf {
+       private final String realm;
+       private final CredVal rbac;
+       
+       
+       public OBasicHttpTaf(final PropAccess access, final CredVal rbac, final String realm, final String token_url, final String introspect_url) throws CadiException {
+               super(access, token_url,introspect_url);
+               this.rbac = rbac;
+               this.realm = realm;
+       }
+       
+       /**
+        * 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
+               final String user;
+               String password=null;
+               byte[] cred=null;
+               if(req instanceof BasicCred) {
+                       BasicCred bc = (BasicCred)req;
+                       user = bc.getUser();
+                       cred = bc.getCred();
+               } else {
+                       String authz = req.getHeader("Authorization");
+                       if(authz != null && authz.startsWith("Basic ")) {
+                               if(!req.isSecure()) {
+                                       access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
+                               }
+                               try {
+                                       String temp = Symm.base64noSplit.decode(authz.substring(6));
+                                       int colon = temp.lastIndexOf(':');
+                                       if(colon>0) {
+                                               user = temp.substring(0,colon);
+                                               password = temp.substring(colon+1);
+                                       } else {
+                                               access.printf(Level.AUDIT,"Malformed BasicAuth entry ip=%s, entry=%s",req.getRemoteAddr(),
+                                                               access.encrypt(temp));
+                                               return new BasicHttpTafResp(access,null,"Malformed BasicAuth entry",RESP.FAIL,resp,realm,false);
+                                       }
+                                       if(!rbac.validate(user,Type.PASSWORD,password.getBytes(),req)) {
+                                               return new BasicHttpTafResp(access,null,buildMsg(null,req,"user/pass combo invalid for ",user,"from",req.getRemoteAddr()), 
+                                                               RESP.TRY_AUTHENTICATING,resp,realm,true);
+                                       }
+                               } catch (IOException e) {
+                                       access.log(e, ERROR_GETTING_TOKEN_CLIENT);
+                                       return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.FAIL,resp,realm,false);
+                               }
+                       } else {
+                               return new BasicHttpTafResp(access,null,"Not a Basic Auth",RESP.TRY_ANOTHER_TAF,resp,realm,false);
+                       }
+               }
+
+               try {
+                       if(password==null && cred!=null) {
+                               password = new String(cred);
+                               cred = Hash.hashSHA256(cred);
+                       } else if(password!=null && cred==null) {
+                               cred = Hash.hashSHA256(password.getBytes());
+                       }
+                       Pooled<TokenClient> pclient = tokenClientPool.get();
+                       try {
+                               pclient.content.password(user, password);
+                               String scope=FQI.reverseDomain(client_id);
+                               Result<TimedToken> rtt = pclient.content.getToken('B',scope);
+                               if(rtt.isOK()) {
+                                       if(rtt.value.expired()) {
+                                               return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: Token Expired",RESP.FAIL,resp,realm,true);
+                                       } else {
+                                               TimedToken tt = rtt.value;
+                                               Result<OAuth2Principal> prin = tkMgr.toPrincipal(tt.getAccessToken(), cred);
+                                               if(prin.isOK()) {
+                                                       return new BasicHttpTafResp(access,prin.value,"BasicAuth/OAuth Token Authentication",RESP.IS_AUTHENTICATED,resp,realm,true);
+                                               } else {
+                                                       return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + prin.code + ' ' + prin.error,RESP.FAIL,resp,realm,true);
+                                               }
+                                       }
+                               } else {
+                                       return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + rtt.code + ' ' + rtt.error,RESP.FAIL,resp,realm,true);
+                               }
+                       } finally {
+                               pclient.done();
+                       }                               
+               } catch (APIException | CadiException | LocatorException | NoSuchAlgorithmException e) {
+                       access.log(e, ERROR_GETTING_TOKEN_CLIENT);
+                       return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.TRY_ANOTHER_TAF,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();
+       }
+
+       @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/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java
new file mode 100644 (file)
index 0000000..74d88fc
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.olur;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.oauth.AbsOTafLur;
+import org.onap.aaf.cadi.oauth.OAuth2Principal;
+import org.onap.aaf.cadi.oauth.TimedToken;
+import org.onap.aaf.cadi.oauth.TokenClient;
+import org.onap.aaf.cadi.oauth.TokenPerm;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Split;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+
+public class OLur extends AbsOTafLur implements Lur {
+       public OLur(PropAccess access, final String token_url, final String introspect_url) throws APIException, CadiException {
+               super(access, token_url, introspect_url);
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#fish(java.security.Principal, org.onap.aaf.cadi.Permission)
+        */
+       @Override
+       public boolean fish(Principal bait, Permission pond) {
+               TokenPerm tp;
+               if(bait instanceof OAuth2Principal) {
+                       OAuth2Principal oa2p = (OAuth2Principal)bait;
+                       tp = oa2p.tokenPerm();
+               } else {
+                       tp=null;
+               }
+               if(tp==null) { 
+                       // if no Token Perm preset, get
+                       try {
+                               Pooled<TokenClient> tcp = tokenClientPool.get();
+                               try {
+                                       TokenClient tc = tcp.content;
+                                       tc.username(bait.getName());
+                                       Result<TimedToken> rtt = tc.getToken(Kind.getKind(bait),tc.defaultScope());
+                                       if(rtt.isOK()) {
+                                               Result<TokenPerm> rtp = tkMgr.get(rtt.value.getAccessToken(), bait.getName().getBytes());
+                                               if(rtp.isOK()) {
+                                                       tp = rtp.value;
+                                               }
+                                       }
+                               } finally {
+                                       tcp.done();
+                               }
+                       } catch (APIException | LocatorException | CadiException e) {
+                               access.log(Level.ERROR, "Unable to Get a Token: " + e.getMessage());
+                       }
+               }
+               if(tp!=null) {
+                       if(tkMgr.access.willLog(Level.DEBUG)) {
+                               StringBuilder sb = new StringBuilder("AAF Permissions for user ");
+                               sb.append(bait.getName());
+                               sb.append(", from token ");                     
+                               sb.append(tp.get().getAccessToken());
+                               for (AAFPermission p : tp.perms()) {
+                                       sb.append("\n\t");
+                                       sb.append(p.getName());
+                                       sb.append('|');
+                                       sb.append(p.getInstance());
+                                       sb.append('|');
+                                       sb.append(p.getAction());
+                               }
+                               sb.append('\n');
+                               access.log(Level.DEBUG, sb);
+                       }
+                       for (AAFPermission p : tp.perms()) {
+                               if (p.match(pond)) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#fishAll(java.security.Principal, java.util.List)
+        */
+       @Override
+       public void fishAll(Principal bait, List<Permission> permissions) {
+               if(bait instanceof OAuth2Principal) {
+                       for (AAFPermission p : ((OAuth2Principal)bait).tokenPerm().perms()) {
+                               permissions.add(p);
+                       }
+               }               
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#handlesExclusively(org.onap.aaf.cadi.Permission)
+        */
+       @Override
+       public boolean handlesExclusively(Permission pond) {
+               return false;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#handles(java.security.Principal)
+        */
+       @Override
+       public boolean handles(Principal principal) {
+               return principal instanceof OAuth2Principal;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String)
+        */
+       @Override
+       public Permission createPerm(final String p) {
+               String[] s = Split.split('|',p);
+               if(s!=null && s.length==3) {
+                       return new AAFPermission(s[0],s[1],s[2]);
+               } else {
+                       return null;
+               }
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java
new file mode 100644 (file)
index 0000000..d9ea017
--- /dev/null
@@ -0,0 +1,301 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.persist;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.client.Holder;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+public abstract class Persist<T,CT extends Persistable<T>> extends PersistFile {
+       private static final long ONE_DAY = 86400000;
+       private static final long CLEAN_CHECK = 2*60*1000; // check every 2 mins
+       private static Timer clean;
+
+       // store all the directories to review
+       // No Concurrent HashSet, or at least, it is all implemented with HashMap in older versions
+       private static Queue<Persist<?,?>> allPersists = new ConcurrentLinkedQueue<Persist<?,?>>();
+       
+       private Map<String,CT> tmap;
+       protected RosettaEnv env;
+       private RosettaDF<T> df;
+
+       
+       public Persist(Access access, RosettaEnv env, Class<T> cls, String sub_dir) throws CadiException, APIException {
+               super(access, sub_dir);
+               this.env = env;
+               df = env.newDataFactory(cls);
+               tmap = new ConcurrentHashMap<String, CT>();
+               synchronized(Persist.class) {
+                       if(clean==null) {
+                               clean = new Timer(true);
+                               clean.schedule(new Clean(access), 20000, CLEAN_CHECK);
+                       }
+               }
+               allPersists.add(this);
+       }
+       
+       public void close() {
+               allPersists.remove(this);
+       }
+       
+       protected abstract CT newCacheable(T t, long expires_secsFrom1970, byte[] hash, Path path) throws APIException, IOException;
+
+       public RosettaDF<T> getDF() {
+               return df;
+       }
+       public Result<CT> get(final String key, final byte[] hash, Loader<CT> rl) throws CadiException, APIException, LocatorException {
+               if(key==null) {
+                       return null;
+               }
+               Holder<Path> hp = new Holder<Path>(null);
+               CT ct = tmap.get(key);
+               // Make sure cached Item is synced with Disk, but only even Minute to save Disk hits
+               if(ct!=null && ct.checkSyncTime()) { // check File Time only every SYNC Period (2 min)
+                       if(ct.hasBeenTouched()) {
+                               tmap.remove(key);
+                               ct = null;
+                               access.log(Level.DEBUG,"File for",key,"has been touched, removing memory entry");
+                       }
+               }
+
+               // If not currently in memory, check with Disk (which might have been updated by other processes)
+               if(ct==null) {
+                       Holder<Long> hl = new Holder<Long>(0L);
+                       T t;
+                       if((t = readDisk(df, hash, key, hp, hl))!=null) {
+                               try {
+                                       if((ct = newCacheable(t,hl.get(),hash,hp.get()))!=null) {
+                                               tmap.put(key, ct);
+                                       }
+                                       access.log(Level.DEBUG,"Read Token from",key);
+                               } catch (IOException e) {
+                                       access.log(e,"Reading Token from",key);
+                               }
+                       } // if not read, then ct still==null
+                       
+                       // If not in memory, or on disk, get from Remote... IF reloadable (meaning, isn't hitting too often, etc).
+                       if(ct==null || ct.checkReloadable()) {
+                               // Load from external (if makes sense)
+                               Result<CT> rtp = rl.load(key);
+                               if(rtp.isOK()) {
+                                       ct = rtp.value;
+                                       try {
+                                               Path p = getPath(key);
+                                               writeDisk(df, ct.get(),ct.getHash(),p,ct.expires());
+                                               access.log(Level.DEBUG, "Writing token",key);
+                                       } catch(CadiException e) {
+                                               throw e;
+                                       } catch (Exception e) {
+                                               throw new CadiException(e);
+                                       }
+                               } else {
+                                       return Result.err(rtp);
+                               }
+                       }
+                       
+                       if(ct!=null) {
+                               tmap.put(key, ct);
+                       }
+               } else {
+                       access.log(Level.DEBUG,"Found token in memory",key);
+               }
+               // ct can only be not-null here
+               ct.inc();
+               return Result.ok(200,ct);
+       }
+
+       public void put(String key, CT ct) throws CadiException {
+               writeDisk(df, ct.get(), ct.getHash(), key, ct.expires());
+               tmap.put(key,ct);
+       }
+       
+       public void delete(String key) {
+               tmap.remove(key);
+               deleteFromDisk(key);
+       }
+
+       public interface Loader<CT> {
+               Result<CT> load(String key) throws APIException, CadiException, LocatorException;  
+       }
+
+       /**
+        * 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 static final class Clean extends TimerTask {
+               private final Access access;
+               private long hourly;
+               
+               public Clean(Access access) {
+                       this.access = access;
+                       hourly=0;
+               }
+               
+               private static class Metrics {
+                       public int mexists = 0, dexists=0;
+                       public int mremoved = 0, dremoved=0;
+               }
+               
+               public void run() {
+                       final long now = System.currentTimeMillis();
+                       final long dayFromNow = now + ONE_DAY;
+                       final Metrics metrics = new Metrics();
+                       for(final Persist<?,?> persist : allPersists) {
+                               // Clear memory
+                               if(access.willLog(Level.DEBUG)) {
+                                       access.log(Level.DEBUG, "Persist: Cleaning memory cache for",persist.tokenPath.toAbsolutePath());
+                               }
+                               for(Entry<String, ?> es : persist.tmap.entrySet()) {
+                                       ++metrics.mexists;
+                                       Persistable<?> p = (Persistable<?>)es.getValue();
+                                       if(p.checkSyncTime()) {
+                                               if(p.count()==0) {
+                                                       ++metrics.mremoved;
+                                                       persist.tmap.remove(es.getKey());
+                                                       access.printf(Level.DEBUG, "Persist: removed cached item %s from memory\n", es.getKey());
+                                               } else {
+                                                       p.clearCount();
+                                               }
+                                       } else if(Files.exists(p.path())) {
+                                               
+                                       }
+                               }
+                               // Clear disk
+                               try {
+                                       final StringBuilder sb = new StringBuilder();
+                                       Files.walkFileTree(persist.tokenPath, new FileVisitor<Path>() {
+                                               @Override
+                                               public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                                                       sb.setLength(0);
+                                                       sb.append("Persist: Cleaning files from ");
+                                                       sb.append(dir.toAbsolutePath());
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+
+                                               @Override
+                                               public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                                                       if(attrs.isRegularFile()) {
+                                                               ++metrics.dexists;
+                                                               try {
+
+                                                                       long exp = persist.readExpiration(file)*1000; // readExpiration is seconds from 1970
+                                                                       if(now > exp) {  // cover for bad token
+                                                                               sb.append("\n\tFile ");
+                                                                               sb.append(file.getFileName());
+                                                                               sb.append(" expired ");
+                                                                               sb.append(Chrono.dateTime(new Date(exp)));
+                                                                               persist.deleteFromDisk(file);
+                                                                               ++metrics.dremoved;
+                                                                       } else if(exp > dayFromNow) {
+                                                                               sb.append("\n\tFile ");
+                                                                               sb.append(file.toString());
+                                                                               sb.append(" data corrupted.");
+                                                                               persist.deleteFromDisk(file);
+                                                                               ++metrics.dremoved;
+                                                                       }
+                                                               } catch (CadiException e) {
+                                                                       sb.append("\n\tError reading File ");
+                                                                       sb.append(file.toString());
+                                                                       sb.append(". ");
+                                                                       sb.append(e.getMessage());
+                                                                       ++metrics.dremoved;
+                                                               }
+                                                               
+                                                       }
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+
+                                               @Override
+                                               public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                                                       access.log(Level.ERROR,"Error visiting file %s (%s)\n",file.toString(),exc.getMessage());
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+
+                                               @Override
+                                               public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                                                       access.log(Level.DEBUG, sb);
+                                                       return FileVisitResult.CONTINUE;
+                                               }
+                                       
+                                       });
+                               } catch (IOException e) {
+                                       access.log(e, "Exception while cleaning Persistance");
+                               }
+                               
+                       }
+                       
+                       // We want to print some activity of Persistence Check at least hourly, even if no activity has occurred, but not litter the log if nothing is happening
+                       boolean go=false;
+                       Level level=Level.WARN;
+                       if(access.willLog(Level.INFO)) {
+                               go = true;
+                               level=Level.INFO;
+                       } else if(access.willLog(Level.WARN)) {
+                               go = metrics.mremoved>0 || metrics.dremoved>0 || --hourly <= 0;
+                       }
+                       
+                       if(go) {
+                               access.printf(level, "Persist Cache: removed %d of %d items from memory and %d of %d from disk", 
+                                       metrics.mremoved, metrics.mexists, metrics.dremoved, metrics.dexists);
+                               hourly = 3600000/CLEAN_CHECK;
+                       }
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see java.lang.Object#finalize()
+        */
+       @Override
+       protected void finalize() throws Throwable {
+               close(); // can call twice.
+       }
+
+       
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java
new file mode 100644 (file)
index 0000000..1fcf043
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.persist;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Symm.Encryption;
+import org.onap.aaf.cadi.client.Holder;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+public class PersistFile {
+
+       private static final String HASH_NO_MATCH = "Hash does not match in Persistence";
+       
+       protected static Symm symm;
+       public Access access;
+       protected final Path tokenPath;
+       protected final String tokenDir;
+       private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
+       
+       public PersistFile(Access access, String sub_dir) throws CadiException, APIException {
+               this.access = access;
+               tokenPath = Paths.get(access.getProperty(Config.CADI_TOKEN_DIR,"tokens"), sub_dir);
+               try {
+                       if(!Files.exists(tokenPath)) {
+                               if(isWindows) {
+                                       // Sorry Windows users, you need to secure your own paths
+                                       Files.createDirectories(tokenPath);
+                               } else {
+                                       Set<PosixFilePermission> spfp = PosixFilePermissions.fromString("rwxr-x---");
+                                       Files.createDirectories(tokenPath,PosixFilePermissions.asFileAttribute(spfp));
+                               }
+                       }
+                       tokenDir=tokenPath.toRealPath().toString();
+               } catch (IOException e) {
+                       throw new CadiException(e);
+               }
+               synchronized(HASH_NO_MATCH) {
+                       if(symm==null) {
+                               symm = Symm.obtain(access);
+                       }
+               }
+       }
+
+       public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final String filename, final long expires) throws CadiException {
+               return writeDisk(df,t,cred,Paths.get(tokenDir,filename),expires);
+       }
+
+       public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final Path target, final long expires) throws CadiException {
+               // Make sure File is completely written before making accessible on disk... avoid corruption.
+               try {
+                       Path tpath = Files.createTempFile(tokenPath,target.getFileName().toString(), ".tmp");
+                       final OutputStream dos = Files.newOutputStream(tpath, StandardOpenOption.CREATE,StandardOpenOption.WRITE);
+                               try {
+                               // Write Expires so that we can read unencrypted.
+                               for(int i=0;i<Long.SIZE;i+=8) {
+                                       dos.write((byte)((expires>>i)&0xFF));
+                               }
+
+                               symm.exec(new Symm.SyncExec<Void>() {
+                                       @Override
+                                       public Void exec(Encryption enc) throws Exception {
+                                               CipherOutputStream os = enc.outputStream(dos, true);
+                                               try {
+                                                       int size = cred==null?0:cred.length;
+                                                       for(int i=0;i<Integer.SIZE;i+=8) {
+                                                               os.write((byte)((size>>i)&0xFF));
+                                                       }
+                                                       if(cred!=null) {
+                                                               os.write(cred);
+                                                       }
+                                                       df.newData().load(t).to(os);
+                                               } finally {
+                                                       // Note: Someone on the Web noticed that using a DataOutputStream would not full close out without a flush first, 
+                                                       // leaving files open.
+                                                       try {
+                                                               os.flush();
+                                                       } catch (IOException e) {
+                                                               access.log(Level.INFO, "Note: Caught Exeption while flushing CipherStream.  Handled.");
+                                                       }
+                                                       try {
+                                                               os.close();
+                                                       } catch (IOException e) {
+                                                               access.log(Level.INFO, "Note: Caught Exeption while closing CipherStream.  Handled.");
+                                                       }
+                                               }
+                                               return null;
+                                       }
+                               });
+                       } catch(Exception e) {
+                               throw new CadiException(e);
+                       } finally {
+                               dos.close();
+                       }
+                       return Files.move(tpath, target, StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);
+               } catch (IOException e) {
+                       throw new CadiException(e);
+               }
+
+       }
+
+       public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final String filename,final Holder<Path> hp, final Holder<Long> hl) throws CadiException {
+               if(hp.get()==null) {
+                       hp.set(Paths.get(tokenDir,filename));
+               }
+               return readDisk(df,cred,hp.get(),hl);
+       }
+       
+       public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final Path target, final Holder<Long> hexpired) throws CadiException {
+               // Try from Disk
+               T t = null;
+               if(Files.exists(target)) {
+                       try {
+                               final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
+                               try {
+                                       // Read Expired unencrypted
+                                       long exp=0;
+                                       for(int i=0;i<Long.SIZE;i+=8) {
+                                               exp |= ((long)is.read()<<i);
+                                       }
+                                       hexpired.set(exp);
+                               
+                                       t = symm.exec(new Symm.SyncExec<T>() {
+                                               @Override
+                                               public T exec(Encryption enc) throws Exception {
+                                                       CipherInputStream dis = enc.inputStream(is,false);
+                                                       try {
+                                                               int size=0;
+                                                               for(int i=0;i<Integer.SIZE;i+=8) {
+                                                                       size |= ((int)dis.read()<<i);
+                                                               }
+                                                               if(size>256) {
+                                                                       throw new CadiException("Invalid size in Token Persistence");
+                                                               } else if(cred!=null && size!=cred.length) {
+                                                                       throw new CadiException(HASH_NO_MATCH);
+                                                               }
+                                                               byte[] array = new byte[size];
+                                                               dis.read(array);
+                                                               for(int i=0;i<size;++i) {
+                                                                       if(cred[i]!=array[i]) {
+                                                                               throw new CadiException(HASH_NO_MATCH);
+                                                                       }
+                                                               }
+                                                               
+                                                               return df.newData().load(dis).asObject();
+                                                       } finally {
+                                                               dis.close();
+                                                       }
+                                               }
+                                       });
+                               } finally {
+                                       is.close();
+                               }
+                       } catch (NoSuchFileException e) { 
+                               return t;
+                       } catch (Exception e) {
+                               throw new CadiException(e);
+                       }
+               }
+               return t;
+       }
+       
+       public long readExpiration(final Path target) throws CadiException {
+               long exp=0L;
+               if(Files.exists(target)) {
+                       try {
+                               final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
+                               try {
+                                       for(int i=0;i<Long.SIZE;i+=8) {
+                                               exp |= ((long)is.read()<<i);
+                                       }
+                               } finally {
+                                       is.close();
+                               }
+                               return exp;
+                       } catch (Exception e) {
+                               throw new CadiException(e);
+                       }
+               }
+               return exp;
+       }
+
+       public void deleteFromDisk(Path path) {
+               try {
+                       Files.deleteIfExists(path);
+               } catch (IOException e) {
+                       access.log(Level.ERROR, e);
+               }
+       }
+
+       public void deleteFromDisk(String token) {
+               Path tpath = Paths.get(tokenDir,token);
+               try {
+                       Files.deleteIfExists(tpath);
+               } catch (IOException e) {
+                       access.log(Level.ERROR, e);
+               }
+       }
+
+       public Path getPath(String filename) {
+               return Paths.get(tokenDir,filename);
+       }
+       
+       public FileTime getFileTime(String filename, Holder<Path> hp) throws IOException {
+               Path p = hp.get();
+               if(p==null) {
+                       hp.set(p=Paths.get(tokenDir,filename));
+               }
+               return Files.getLastModifiedTime(p);
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java
new file mode 100644 (file)
index 0000000..6543779
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.persist;
+
+import java.nio.file.Path;
+
+public interface Persistable<T> {
+       public boolean checkSyncTime();
+       public boolean checkReloadable();
+       public void inc();
+       public int count();
+       public void clearCount();
+       public boolean hasBeenTouched();
+       public long expires(); // seconds from 1970
+       public boolean expired();
+       public byte[] getHash();
+       public boolean match(byte[] hashIn);
+       public T get();
+       public Path path();
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java
new file mode 100644 (file)
index 0000000..f75527d
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.persist;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+
+import org.onap.aaf.cadi.Access.Level;
+
+public class Persisting<T> implements Persistable<T> {
+       private static final byte[] EMPTY = new byte[0];
+       private final byte[] hash; // need to be able to validate disk entry
+
+       private static final long SYNC_TIME = 1000*60*1; // Checking File change max 1 min
+       private FileTime lastTouched;
+       private int count;
+       private long expires;
+       private long nextCheck;
+       private T t;
+       private Path path;
+       private Persist<T, ?> persist;
+       
+       public Persisting(Persist<T,?> p, T t, long expiresSecsFrom1970, byte[] hash, Path path) {
+               persist = p;
+               this.t=t;
+               expires = expiresSecsFrom1970;
+               this.path = path;
+               try {
+                       lastTouched = Files.getLastModifiedTime(path);
+               } catch (IOException e) {
+                       lastTouched = null;
+               }
+               count=0;
+               nextCheck=0;
+               if(hash==null) {
+                       this.hash = EMPTY;
+               } else {
+                       this.hash = hash;
+               }
+       }
+
+       @Override
+       public T get() {
+               return t;
+       }
+
+       @Override
+       public long expires() {
+               return expires;
+       }
+
+       @Override
+       public boolean expired() {
+               return System.currentTimeMillis()/1000>expires;
+       }
+
+       @Override
+       public boolean hasBeenTouched() {
+               try {
+                       FileTime modT = Files.getLastModifiedTime(path);
+                       if(lastTouched==null) {
+                               lastTouched = modT;
+                               return true;
+                       } else {
+                               return !modT.equals(lastTouched);
+                       }
+               } catch (NoSuchFileException e) {
+                       persist.access.log(Level.DEBUG, "File not found " +  e.getMessage() + ", this is ok, marking as touched.");
+                       return true;
+               } catch (IOException e) {
+                       persist.access.log(e, "Accessing File Time");
+                       return true;
+               }
+       }
+
+       @Override
+       public synchronized boolean checkSyncTime() {
+               long temp=System.currentTimeMillis();
+               if(nextCheck==0 || nextCheck<temp) {
+                       nextCheck = temp+SYNC_TIME;
+                       return true;
+               }
+               return false;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.oauth.Persistable#checkReloadTime()
+        */
+       @Override
+       public boolean checkReloadable() {
+               //TODO other elements to add here... 
+               // Ideas:  Is it valid?
+               //         if not, How many times has it been checked in the last minute
+               if(expired()) {
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       @Override
+       public byte[] getHash() {
+               return hash;
+       }
+
+       @Override
+       public boolean match(byte[] hashIn) {
+               if(hash==null || hashIn==null || hash.length!=hashIn.length) {
+                       return false;
+               }
+               for(int i=0;i<hashIn.length;++i) {
+                       if(hash[i]!=hashIn[i]) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       @Override
+       public synchronized void inc() {
+               ++count;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.oauth.Cacheable#count()
+        */
+       @Override
+       public int count() {
+               return count;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.oauth.Persistable#clearCount()
+        */
+       @Override
+       public synchronized void clearCount() {
+               count=0;
+       }
+
+       @Override
+       public Path path() {
+               return path;
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java
new file mode 100644 (file)
index 0000000..17e850f
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.register;
+
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.misc.env.impl.BasicEnv;
+
+public interface Registrant<ENV extends BasicEnv> {
+       public Result<Void> update(ENV env);
+       public Result<Void> cancel(ENV env);
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java
new file mode 100644 (file)
index 0000000..e3ae85d
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.register;
+
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.misc.env.impl.BasicEnv;
+
+public class Registrar<ENV extends BasicEnv> {
+       private static final String REGISTRAR = "Registrar";
+       private static final long INTERVAL = 15*60*1000; // 15 mins
+       private static final long START = 3000; // Start in 3 seconds
+       private Deque<Registrant<ENV>> registrants;
+       private Timer timer, erroringTimer;
+
+       public Registrar(final ENV env, boolean shutdownHook) {
+               registrants = new ConcurrentLinkedDeque<Registrant<ENV>>();
+
+               erroringTimer = null;
+               timer = new Timer(REGISTRAR,true);
+               timer.schedule(new RegistrationTimerTask(env), START, INTERVAL); 
+               
+               if(shutdownHook) {
+                       Runtime.getRuntime().addShutdownHook(new Thread() {
+                               public void run() {
+                                       close(env);
+                               }
+                       });
+               }
+       }
+       
+       private class RegistrationTimerTask extends TimerTask {
+               private final ENV env;
+               public RegistrationTimerTask(ENV env) {
+                       this.env = env;
+               }
+               @Override
+               public void run() {
+                       for(Iterator<Registrant<ENV>> iter = registrants.iterator(); iter.hasNext();) {
+                               Registrant<ENV> reg = iter.next();
+                               Result<Void> rv = reg.update(env);
+                               synchronized(REGISTRAR) {
+                                       if(rv.isOK()) {
+                                               if(erroringTimer!=null) {
+                                                       erroringTimer.cancel();
+                                                       erroringTimer = null;
+                                               }
+                                       } else {
+                                               // Account for different Registrations not being to same place
+                                               if(erroringTimer==null) {
+                                                       erroringTimer =  new Timer(REGISTRAR + " error re-check ",true);
+                                                       erroringTimer.schedule(new RegistrationTimerTask(env),20000,20000);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       public void register(Registrant<ENV> r) {
+               registrants.addLast(r);
+       }
+       
+       public void deregister(Registrant<ENV> r) {
+               registrants.remove(r);
+       }
+
+       public void close(ENV env) {
+               timer.cancel();
+
+               Registrant<ENV> r;
+               while(registrants.peek()!=null) {
+                       r = registrants.pop();
+                       r.cancel(env);
+               }
+       }
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java
new file mode 100644 (file)
index 0000000..6c1d668
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.register;
+
+import java.net.HttpURLConnection;
+import java.net.Inet4Address;
+import java.net.URI;
+import java.net.UnknownHostException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+import org.onap.aaf.cadi.util.Split;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.impl.BasicEnv;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+import locate.v1_0.MgmtEndpoint;
+import locate.v1_0.MgmtEndpoints;
+
+public class RemoteRegistrant<ENV extends BasicEnv> implements Registrant<ENV> {
+       private final MgmtEndpoint mep;
+       private final MgmtEndpoints meps;
+       private final AAFCon<HttpURLConnection> aafcon;
+       private final RosettaDF<MgmtEndpoints> mgmtEndpointsDF;
+       private final Locator<URI> locator;
+       private final Access access;
+       private final int timeout;
+
+       @SafeVarargs
+       public RemoteRegistrant(AAFCon<HttpURLConnection> aafcon, String name, String version, int port, RemoteRegistrant<ENV> ... others) throws CadiException, LocatorException {
+               this.aafcon = aafcon;
+               access = aafcon.access;
+               try {
+                       mgmtEndpointsDF = aafcon.env.newDataFactory(MgmtEndpoints.class);
+               } catch (APIException e1) {
+                       throw new CadiException(e1);
+               }
+               timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
+               String aaf_locate = access.getProperty(Config.AAF_LOCATE_URL,null);
+               if(aaf_locate==null) {
+                       throw new CadiException(Config.AAF_LOCATE_URL + " is required.");
+               } else {
+                       // Note: want Property Locator, not AAFLocator, because we want the core service, not what it can find
+                       locator = new PropertyLocator(aaf_locate);
+               }
+               
+               mep = new MgmtEndpoint();
+               mep.setName(name);
+               mep.setPort(port);
+               
+               try {
+                       String latitude = access.getProperty(Config.CADI_LATITUDE, null);
+                       if(latitude==null) {
+                               latitude = access.getProperty("AFT_LATITUDE", null);
+                       }
+                       String longitude = access.getProperty(Config.CADI_LONGITUDE, null);
+                       if(longitude==null) {
+                               longitude = access.getProperty("AFT_LONGITUDE", null);
+                       }
+                       if(latitude==null || longitude==null) {
+                               throw new CadiException(Config.CADI_LATITUDE + " and " + Config.CADI_LONGITUDE + " is required");
+                       } else {
+                               mep.setLatitude(Float.parseFloat(latitude));
+                               mep.setLongitude(Float.parseFloat(longitude));
+                       }
+                       String split[] = Split.split('.', version);
+                       mep.setPkg(split.length>3?Integer.parseInt(split[3]):0);
+                       mep.setPatch(split.length>2?Integer.parseInt(split[2]):0);
+                       mep.setMinor(split.length>1?Integer.parseInt(split[1]):0);
+                       mep.setMajor(split.length>0?Integer.parseInt(split[0]):0);
+                       
+                       mep.setHostname(access.getProperty(Config.HOSTNAME, Inet4Address.getLocalHost().getHostName()));
+                       String subprotocols = access.getProperty(Config.CADI_PROTOCOLS, null);
+                       if(subprotocols==null) {
+                               mep.setProtocol("http");
+                       } else {
+                               mep.setProtocol("https");
+                               for(String s : Split.split(',', subprotocols)) {
+                                       mep.getSubprotocol().add(s);
+                               }
+                       }
+               } catch (NumberFormatException | UnknownHostException e) {
+                       throw new CadiException("Error extracting Data from Properties for Registrar",e);
+               }
+               meps = new MgmtEndpoints();
+               meps.getMgmtEndpoint().add(mep);
+               for(RemoteRegistrant<ENV> rr : others) {
+                       meps.getMgmtEndpoint().add(rr.mep);
+               }
+       }
+       
+       @Override
+       public Result<Void> update(ENV env) {
+               try {
+                       Rcli<?> client = aafcon.client(locator);
+                       try {
+                               Future<MgmtEndpoints> fup = client.update("/registration",mgmtEndpointsDF,meps);
+                               if(fup.get(timeout)) {
+                                       access.log(Level.INFO, "Registration complete to",client.getURI());
+                                       return Result.ok(fup.code(),null);
+                               } else {
+                                       access.log(Level.ERROR,"Error registering to AAF Locator on ", client.getURI());
+                                       return Result.err(fup.code(),fup.body());
+                               }
+                       } catch (APIException e) {
+                               access.log(e, "Error registering service to AAF Locator");
+                               return Result.err(503,e.getMessage());
+                       }
+                       
+               } catch (CadiException e) {
+                       return Result.err(503,e.getMessage());
+               }
+       }
+
+       @Override
+       public Result<Void> cancel(ENV env) {
+               try {
+                       Rcli<?> client = aafcon.client(locator);
+                       try {
+                               Future<MgmtEndpoints> fup = client.delete("/registration",mgmtEndpointsDF,meps);
+                               if(fup.get(timeout)) {
+                                       access.log(Level.INFO, "Deregistration complete on",client.getURI());
+                                       return Result.ok(fup.code(),null);
+                               } else {
+                                       return Result.err(fup.code(),fup.body());
+                               }
+                       } catch (APIException e) {
+                               access.log(e, "Error deregistering service on AAF Locator");
+                               return Result.err(503,e.getMessage());
+                       }
+                       
+               } catch (CadiException e) {
+                       return Result.err(503,e.getMessage());
+               }
+       }
+
+}
diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java
new file mode 100644 (file)
index 0000000..0241fe5
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.sso;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.util.MyConsole;
+import org.onap.aaf.cadi.util.SubStandardConsole;
+import org.onap.aaf.cadi.util.TheConsole;
+
+
+public class AAFSSO {
+       public static final MyConsole  cons = TheConsole.implemented()?new TheConsole():new SubStandardConsole();
+       
+       private Properties diskprops = null; // use for temp storing User/Password on disk
+       private File dot_aaf = null, sso=null; // instantiated, if ever, with diskprops
+       
+       boolean removeSSO=false;
+       boolean loginOnly = false;
+       private PropAccess access;
+       private StringBuilder err;
+       private String user,encrypted_pass;
+       private boolean use_X509;
+
+       private PrintStream os, stdout=null,stderr=null;
+
+       private Method close;
+
+       public AAFSSO(String[] args) throws IOException, CadiException {
+               List<String> larg = new ArrayList<String>(args.length);
+
+               // Cover for bash's need to escape *.. (\\*)
+               // also, remove SSO if required
+               for (int i = 0; i < args.length; ++i) {
+                       if ("\\*".equals(args[i])) {
+                               args[i] = "*";
+                       }
+                       
+                       if("-logout".equalsIgnoreCase(args[i])) {
+                               removeSSO=true;
+                       } else if("-login".equalsIgnoreCase(args[i])) {
+                               loginOnly = true;
+                       } else {
+                               larg.add(args[i]);
+                       }
+               }
+               
+               String[] nargs = new String[larg.size()];
+               larg.toArray(nargs);
+
+               dot_aaf = new File(System.getProperty("user.home")+"/.aaf");
+               if(!dot_aaf.exists()) {
+                       dot_aaf.mkdirs();
+               }
+               File f = new File(dot_aaf,"sso.out");
+               os = new PrintStream(new FileOutputStream(f,true));
+               stdout = System.out;
+               stderr = System.err;
+               System.setOut(os);
+               System.setErr(os);
+
+               access = new PropAccess(os,nargs);
+               Config.setDefaultRealm(access);
+
+               user = access.getProperty(Config.AAF_APPID);
+               encrypted_pass = access.getProperty(Config.AAF_APPPASS);
+               
+               File dot_aaf_kf = new File(dot_aaf,"keyfile");
+               
+               sso = new File(dot_aaf,"sso.props");
+               if(removeSSO) {
+                       if(dot_aaf_kf.exists()) {
+                               dot_aaf_kf.setWritable(true,true);
+                               dot_aaf_kf.delete();
+                       }
+                       if(sso.exists()) {
+                               sso.delete();
+                       }
+                       System.out.println("AAF SSO information removed");
+                       System.exit(0);
+               }
+               
+               if(!dot_aaf_kf.exists()) {
+                       FileOutputStream fos = new FileOutputStream(dot_aaf_kf);
+                       try {
+                               fos.write(Symm.keygen());
+                               dot_aaf_kf.setExecutable(false,false);
+                               dot_aaf_kf.setWritable(false,false);
+                               dot_aaf_kf.setReadable(false,false);
+                               dot_aaf_kf.setReadable(true, true);
+                       } finally {
+                               fos.close();
+                       }
+               }
+
+               String keyfile = access.getProperty(Config.CADI_KEYFILE); // in case it's CertificateMan props
+               if(keyfile==null) {
+                       access.setProperty(Config.CADI_KEYFILE, dot_aaf_kf.getAbsolutePath());
+               }
+               
+               String alias = access.getProperty(Config.CADI_ALIAS);
+               if(user==null && alias!=null && access.getProperty(Config.CADI_KEYSTORE_PASSWORD)!=null) {
+                       user = alias;
+                       access.setProperty(Config.AAF_APPID, user);
+                       use_X509 = true;
+               } else {
+                       use_X509 = false;
+                       Symm decryptor = Symm.obtain(dot_aaf_kf);
+                       if (user==null) {
+                               if(sso.exists() && sso.lastModified()>System.currentTimeMillis()-(8*60*60*1000 /* 8 hours */)) {
+                                       String cm_url = access.getProperty(Config.CM_URL); // SSO might overwrite...
+                                       FileInputStream fos = new FileInputStream(sso);
+                                       try {
+                                               access.load(fos);
+                                               user = access.getProperty(Config.AAF_APPID);
+                                               encrypted_pass = access.getProperty(Config.AAF_APPPASS);
+                                               // decrypt with .aaf, and re-encrypt with regular Keyfile
+                                               access.setProperty(Config.AAF_APPPASS, 
+                                                               access.encrypt(decryptor.depass(encrypted_pass)));
+                                               if(cm_url!=null) { //Command line CM_URL Overwrites ssofile.
+                                                       access.setProperty(Config.CM_URL, cm_url);
+                                               }
+                                       } finally {
+                                               fos.close();
+                                       }
+                               } else {
+                                       diskprops = new Properties();
+                                       String realm = Config.getDefaultRealm();
+                                       // Turn on Console Sysout
+                                       System.setOut(stdout);
+                                       user=cons.readLine("aaf_id(%s@%s): ",System.getProperty("user.name"),realm);
+                                       if(user==null) {
+                                               user = System.getProperty("user.name")+'@'+realm;
+                                       } else if(user.length()==0) { // 
+                                               user = System.getProperty("user.name")+'@' + realm;
+                                       } else if(user.indexOf('@')<0 && realm!=null) {
+                                               user = user+'@'+realm;
+                                       }
+                                       access.setProperty(Config.AAF_APPID,user);
+                                       diskprops.setProperty(Config.AAF_APPID,user);
+                                       encrypted_pass = new String(cons.readPassword("aaf_password: "));
+                                       System.setOut(os);
+                                       encrypted_pass = Symm.ENC+decryptor.enpass(encrypted_pass);
+                                       access.setProperty(Config.AAF_APPPASS,encrypted_pass);
+                                       diskprops.setProperty(Config.AAF_APPPASS,encrypted_pass);
+                                       diskprops.setProperty(Config.CADI_KEYFILE, access.getProperty(Config.CADI_KEYFILE));
+                               }
+                       }
+               }
+               if (user == null) {
+                       err = new StringBuilder("Add -D" + Config.AAF_APPID + "=<id> ");
+               }
+       
+               if (encrypted_pass == null && alias==null) {
+                       if (err == null) {
+                               err = new StringBuilder();
+                       } else {
+                               err.append("and ");
+                       }
+                       err.append("-D" + Config.AAF_APPPASS + "=<passwd> ");
+               }
+       }
+       
+       public void setLogDefault() {
+               access.setLogLevel(PropAccess.DEFAULT);
+               if(stdout!=null) {
+                       System.setOut(stdout);
+               }
+       }
+
+       public void setStdErrDefault() {
+               access.setLogLevel(PropAccess.DEFAULT);
+               if(stderr!=null) {
+                       System.setErr(stderr);
+               }
+       }
+
+       public void setLogDefault(Level level) {
+               access.setLogLevel(level);
+               if(stdout!=null) {
+                       System.setOut(stdout);
+               }
+       }
+       
+       public boolean loginOnly() {
+               return loginOnly;
+       }
+
+       public void addProp(String key, String value) {
+               if(diskprops!=null) {
+                       diskprops.setProperty(key, value);
+               }
+       }
+       
+       public void writeFiles() throws IOException {
+               // Store Creds, if they work 
+               if(diskprops!=null) {
+                       if(!dot_aaf.exists()) {
+                               dot_aaf.mkdirs();
+                       }
+                       FileOutputStream fos = new FileOutputStream(sso);
+                       try {
+                               diskprops.store(fos, "AAF Single Signon");
+                       } finally {
+                               fos.close();
+                               sso.setWritable(false,false);
+                               sso.setExecutable(false,false);
+                               sso.setReadable(false,false);
+                               sso.setReadable(true,true);
+                       }
+               }
+               if(sso!=null) {
+                       sso.setReadable(false,false);
+                       sso.setWritable(false,false);
+                       sso.setExecutable(false,false);
+                       sso.setReadable(true,true);
+                       sso.setWritable(true,true);
+               }
+       }
+
+       public PropAccess access() {
+               return access;
+       }
+
+       public StringBuilder err() {
+               return err;
+       }
+       
+       public String user() {
+               return user;
+       }
+       
+       public String enc_pass() {
+               return encrypted_pass;
+       }
+       
+       public boolean useX509() {
+               return use_X509;
+       }
+       
+       public void close() {
+               if(close!=null) {
+                       try {
+                               close.invoke(null);
+                       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                               // nothing to do here.
+                       }
+                       close = null;
+               }
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/JU_ErrMessageTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/JU_ErrMessageTest.java
new file mode 100644 (file)
index 0000000..15e1162
--- /dev/null
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.aaf.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.aaf.client.ErrMessage;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Error;
+
+public class JU_ErrMessageTest {
+       
+       @Mock
+       private RosettaEnv env;
+       
+       @Mock(answer=Answers.RETURNS_DEEP_STUBS)
+       private RosettaDF<Object> errDF;
+
+       private ErrMessage errMessage;
+
+       private String attErrJson = "key:value";
+       
+       private Error error;
+
+       private Future<?> future;
+       
+       @Before
+       public void setUp() throws Exception {
+               MockitoAnnotations.initMocks(this);
+               
+               when(env.newDataFactory(Error.class)).thenReturn(errDF);
+               
+               future = new Future<Error>() {
+
+                       @Override
+                       public boolean get(int timeout) throws CadiException {
+                               return false;
+                       }
+
+                       @Override
+                       public int code() {
+                               return 0;
+                       }
+
+                       @Override
+                       public String body() {
+                               return "Body";
+                       }
+
+                       @Override
+                       public String header(String tag) {
+                               return "header";
+                       }
+               };
+               
+               error = new Error();
+               error.setMessageId("Error Message Id");
+               error.setText("Error Text");
+               errMessage = new ErrMessage(env);
+               
+               
+       }
+
+       @Test
+       public void testPrintErrMessage() throws APIException {
+               when(errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()).thenReturn(error);
+               
+               errMessage.printErr(System.out, attErrJson);
+       }
+       
+       @Test
+       public void testToMsgJsonErrAttribute() throws APIException {
+               when(errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()).thenReturn(error);
+               
+               StringBuilder sb = new StringBuilder();
+               errMessage.toMsg(sb,attErrJson);
+               
+               assertEquals(sb.toString(),"Error Message Id Error Text");
+       }
+       
+       @Test
+       public void testToMsgFuture() {
+               StringBuilder sb = errMessage.toMsg(future);
+               
+               assertEquals(sb.toString(), "0: Body");
+       }
+
+       
+       @Test
+       public void testToMsgFutureWithoutException() throws APIException {
+               when(errDF.newData().in(TYPE.JSON).load(future.body()).asObject()).thenReturn(error);
+               
+               StringBuilder sb = errMessage.toMsg(future);
+               
+               assertEquals(sb.toString(), "Error Message Id Error Text");
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/JU_CertsMarshalTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/JU_CertsMarshalTest.java
new file mode 100644 (file)
index 0000000..9ac6b27
--- /dev/null
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.aaf.marshal;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.aaf.marshal.CertsMarshal;
+import org.onap.aaf.misc.rosetta.ParseException;
+
+public class JU_CertsMarshalTest {
+
+       @Test
+       public void test() throws ParseException {
+               CertsMarshal marshal = new CertsMarshal();
+               assertNotNull(marshal);
+       }
+
+       @Test
+       public void test2() throws ParseException {
+               CertsMarshal marshal = new CertsMarshal();
+               assertNotNull(marshal);
+       }
+
+       @Test
+       public void test3() throws ParseException {
+               CertsMarshal marshal = new CertsMarshal();
+               assertNotNull(marshal);
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFLocator.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFLocator.java
new file mode 100644 (file)
index 0000000..71d22b8
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.test;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
+import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.misc.env.impl.BasicTrans;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+public class JU_AAFLocator {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               try {
+                       // TODO: Ian [JUnit] This fails because these files don't exist
+                       PropAccess propAccess = new PropAccess("cadi_prop_files=/opt/app/aaf/common/com.att.aaf.common.props:/opt/app/aaf/common/com.att.aaf.props");
+                       SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(propAccess, HttpURLConnection.class);
+                       String alu = propAccess.getProperty(Config.AAF_LOCATE_URL,"https://mithrilcsp.sbc.com:8095/locate");
+                       URI locatorURI = new URI(alu+"/com.att.aaf.service/2.0");
+                       AbsAAFLocator<BasicTrans> al = new AAFLocator(si, locatorURI);
+                       Assert.assertTrue(al.refresh());
+                       Item i = al.first();
+                       i = al.next(i);
+                       i = al.best();
+                       System.out.println("hi");
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       Assert.fail();
+               }
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java
new file mode 100644 (file)
index 0000000..9536cd9
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.aaf.test;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
+import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HBasicAuthSS;
+import org.onap.aaf.cadi.http.HMangr;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.impl.BasicTrans;
+
+public class TestHClient {
+       public static void main(String[] args) {
+               try {
+                       PropAccess access = new PropAccess(args);
+                       String aaf_url = access.getProperty(Config.AAF_URL);
+                       if(aaf_url == null) {
+                               access.log(Level.ERROR, Config.AAF_URL," is required");
+                       } else {
+                               HMangr hman = null;
+                               try {
+                                       SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class);
+                                       AbsAAFLocator<BasicTrans> loc = new AAFLocator(si,new URI(aaf_url));
+                                       for(Item item = loc.first(); item!=null; item=loc.next(item)) {
+                                               System.out.println(loc.get(item));
+                                       }
+                                       SecuritySetter<HttpURLConnection> ss = new HBasicAuthSS(si);
+               //                      SecuritySetter<HttpURLConnection> ss = new X509SS(si, "aaf");
+                                       
+                                       hman = new HMangr(access,loc);
+                                       final String path = String.format("/authz/perms/user/%s",
+                                                       access.getProperty(Config.AAF_APPID,"xx9999@csp.att.com"));
+                                       hman.best(ss, new Retryable<Void>() {
+                                               @Override
+                                               public Void code(Rcli<?> cli) throws APIException, CadiException {
+                                                       Future<String> ft = cli.read(path,"application/json");  
+                                                       if(ft.get(10000)) {
+                                                               System.out.println("Hurray,\n"+ft.body());
+                                                       } else {
+                                                               System.out.println("not quite: " + ft.code());
+                                                       }
+                                                       return null;
+                                               }});
+                               } finally {
+                                       if(hman!=null) {
+                                               hman.close();
+                                       }
+                               }
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+       
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/JU_AAFAuthnTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/JU_AAFAuthnTest.java
new file mode 100644 (file)
index 0000000..5bea198
--- /dev/null
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.aaf.v2_0;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.misc.env.APIException;
+
+public class JU_AAFAuthnTest {
+       
+       @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+       private AAFCon<HttpsURLConnection> con;
+
+       @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+       private Future<String> fp;
+
+       @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+       private AbsUserCache<AAFPermission> cache;
+
+       
+       @Before
+       public void setup() throws APIException, CadiException {
+               MockitoAnnotations.initMocks(this);
+               when(con.client(Config.AAF_DEFAULT_VERSION).read("/authn/basicAuth", "text/plain")).thenReturn(fp);
+       }
+       /*TODO broken JUNIT with MOCKITO
+        * 
+        * reduce size of this test file by looping thru conOfClient instead of copy pasta?
+        * 
+        * These appear to receive data but cannot validate them.  Maybe due to @before
+        * Possible fixes for tomorrow: look into auth,validate, @before mockito, @mock issues?
+        *
+        */
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               assertNotNull(auth.validate("NewUser", "New Password"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+       //TODO broken JUNIT with MOCKITO
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT1() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               
+               assertNotNull(auth.validate("NewUser1", "New Password1"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission1() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+       //TODO broken JUNIT with MOCKITO
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT2() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               
+               assertNotNull(auth.validate("NewUser2", "New Password2"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission2() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+       //TODO broken JUNIT with MOCKITO
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT3() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               
+               assertNotNull(auth.validate("NewUser3", "New Password3"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission3() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+       //TODO broken JUNIT with MOCKITO
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT4() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               
+               assertNotNull(auth.validate("NewUser4", "New Password4"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission4() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+       //TODO broken JUNIT with MOCKITO
+       @Test
+       public void testAAFAuthnAAFConOfCLIENT5() throws Exception {
+               when(fp.get(anyInt())).thenReturn(false);
+               when(fp.code()).thenReturn(401);
+               when(fp.header("WWW-Authenticate")).thenReturn("Basic realm=\"Value\"");
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn();
+               
+               assertNotNull(auth.validate("NewUser5", "New Password5"));;
+       }
+
+       @Test
+       public void testAAFAuthnAAFConOfCLIENTAbsUserCacheOfAAFPermission5() throws Exception {
+               AAFAuthn<HttpsURLConnection> auth = con.newAuthn(cache);
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java
new file mode 100644 (file)
index 0000000..187a4b7
--- /dev/null
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.lur.aaf.test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.aaf.v2_0.AAFTaf;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.locator.DNSLocator;
+import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
+
+import junit.framework.Assert;
+
+public class JU_JMeter {
+       private static AAFConHttp aaf;
+       private static AAFAuthn<HttpURLConnection> aafAuthn;
+       private static AAFLurPerm aafLur;
+       private static ArrayList<Principal> perfIDs;
+       
+       private static AAFTaf<HttpURLConnection> aafTaf;
+       private static PropAccess access;
+
+       @BeforeClass
+       public static void before() throws Exception {
+               if(aafLur==null) {
+                       Properties props = System.getProperties();
+                       props.setProperty("AFT_LATITUDE", "32.780140");
+                       props.setProperty("AFT_LONGITUDE", "-96.800451");
+                       props.setProperty("DME2_EP_REGISTRY_CLASS","DME2FS");
+                       props.setProperty("AFT_DME2_EP_REGISTRY_FS_DIR","/Volumes/Data/src/authz/dme2reg");
+                       props.setProperty("AFT_ENVIRONMENT", "AFTUAT");
+                       props.setProperty("SCLD_PLATFORM", "NON-PROD");
+                       props.setProperty(Config.AAF_URL,"https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=DEV/routeOffer=BAU_SE");
+                       props.setProperty(Config.AAF_CALL_TIMEOUT, "2000");
+                       int timeToLive = 3000;
+                       props.setProperty(Config.AAF_CLEAN_INTERVAL, Integer.toString(timeToLive));
+                       props.setProperty(Config.AAF_HIGH_COUNT, "4");
+
+                       String aafPerfIDs = props.getProperty("AAF_PERF_IDS");
+                       perfIDs = new ArrayList<Principal>();
+                       File perfFile = null;
+                       if(aafPerfIDs!=null) {
+                               perfFile = new File(aafPerfIDs);
+                       }
+
+                       access = new PropAccess();
+                       aaf = new AAFConHttp(access, new DNSLocator(access,"https","localhost","8100"));
+                       aafTaf = new AAFTaf<HttpURLConnection>(aaf,false);
+                       aafLur = aaf.newLur(aafTaf);
+                       aafAuthn = aaf.newAuthn(aafTaf);
+                       aaf.basicAuth("testid@aaf.att.com", "whatever");
+
+                       if(perfFile==null||!perfFile.exists()) {
+                               perfIDs.add(new CachedBasicPrincipal(aafTaf, 
+                                               "Basic dGVzdGlkOndoYXRldmVy", 
+                                               "aaf.att.com",timeToLive));
+                               perfIDs.add(new Princ("ab1234@aaf.att.com")); // Example of Local ID, which isn't looked up
+                       } else {
+                               BufferedReader ir = new BufferedReader(new FileReader(perfFile));
+                               try {
+                                       String line;
+                                       while((line = ir.readLine())!=null) {
+                                               if((line=line.trim()).length()>0)
+                                                       perfIDs.add(new Princ(line));
+                                       }
+                               } finally {
+                                       ir.close();
+                               }
+                       }
+                       Assert.assertNotNull(aafLur);
+               }
+       }
+
+       private static class Princ implements Principal {
+               private String name;
+               public Princ(String name) {
+                       this.name = name;
+               }
+               public String getName() {
+                       return name;
+               }
+               
+       };
+       
+       private static int index = -1;
+       
+       private synchronized Principal getIndex() {
+               if(perfIDs.size()<=++index)index=0;
+               return perfIDs.get(index);
+       }
+       @Test
+       public void test() {
+               try {
+                               aafAuthn.validate("testid@aaf.att.com", "whatever");
+                               List<Permission> perms = new ArrayList<Permission>();
+                               aafLur.fishAll(getIndex(), perms);
+//                             Assert.assertFalse(perms.isEmpty());
+//                             for(Permission p : perms) {
+//                                     //access.log(Access.Level.AUDIT, p.permType());
+//                             }
+               } catch (Exception e) {
+                       StringWriter sw = new StringWriter();
+                       e.printStackTrace(new PrintWriter(sw));
+                       Assert.assertFalse(sw.toString(),true);
+               }
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_Lur2_0Call.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_Lur2_0Call.java
new file mode 100644 (file)
index 0000000..ddc7f00
--- /dev/null
@@ -0,0 +1,573 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.lur.aaf.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Taf.LifeForm;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.aaf.v2_0.AAFTaf;
+import org.onap.aaf.cadi.locator.DNSLocator;
+import org.onap.aaf.cadi.lur.ConfigPrincipal;
+import org.onap.aaf.cadi.lur.LocalPermission;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class JU_Lur2_0Call {
+       private static AAFConHttp aaf;
+       private static PropAccess access;
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+               access = new PropAccess();
+               aaf = new AAFConHttp(access,new DNSLocator(access,"https","localhost","8100"));
+               aaf.basicAuth("testid", "whatever");
+       }
+
+       @Test 
+       public void test() throws Exception {
+       
+               AAFLurPerm aafLur = aaf.newLur();
+
+               Principal pri = new ConfigPrincipal("testid@aaf.att.com","whatever");
+               for (int i = 0; i < 10; ++i) {
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance|write"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|kumquat|write"),false);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance|read"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|kumquat|read"),true);
+                       
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","myInstance","write"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","kumquat","write"),false);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","myInstance","read"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","kumquat","read"),true);
+
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!kum.*|read"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance|!wr*"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance"),true);
+
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","!kum.*","read"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","myInstance","!wr*"),true);
+
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!kum[Qq]uat|read"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!my[iI]nstance|!wr*"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!my[iI]nstance|!wr*"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance|!wr*"),true);
+
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","!kum[Qq]uat","read"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","!my[iI]nstance","!wr*"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","!my[iI]nstance","!wr*"),true);
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service","myInstance","!wr*"),true);
+                       
+
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!my.nstance|!wr*"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|my.nstance|!wr*"),false);
+                       
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|my.nstance|!wr*"),false);
+                       
+                       //Maitrayee, aren't we going to have issues if we do RegExp with "."?
+                       //Is it too expensive to only do Reg Ex in presence of special characters, []{}*, etc? Not sure this helps for GRID.
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|kum.quat|read"),true);
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|!kum..uat|read"),true);
+                       
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance"),true); // ok if Stored Action is "*"
+                       
+                       // Key Evaluations
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|:myCluster:*:!my.*|write"),true); // ok if Stored Action is "*"
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|:myCluster:*|write"),false); // not ok if key lengths don't match "*"
+                       print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|:myCluster:*:myCF|write"),true); // ok if Stored Action is "*"
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service",":myCluster:*:!my.*","write"),true); // ok if Stored Action is "*"
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service",":myCluster:*:myCF","write"),true); // ok if Stored Action is "*"
+                       print(aafLur, pri, new AAFPermission("com.test.JU_Lur2_0Call.service",":myCluster:*","write"),false); // not ok if key lengths don't match
+                       
+               }
+
+               print(aafLur, pri, new LocalPermission("bogus"),false);
+
+//             try {
+//                     Thread.sleep(7000);
+//             } catch (InterruptedException e) {
+//                     e.printStackTrace();
+//             }
+               for (int i = 0; i < 10; ++i)
+                       print(aafLur, pri, new LocalPermission("supergroup"),false);
+
+               System.out.println("All Done");
+       }
+       @Test
+       public void testTaf() throws Exception {
+               AAFTaf<?> aaft = new AAFTaf<HttpURLConnection>(aaf,true);
+               
+               TafResp resp;
+               // No Header
+               resp = aaft.validate(LifeForm.CBLF, new Req(), null);
+               assertEquals(TafResp.RESP.TRY_AUTHENTICATING, resp.isAuthenticated());
+
+               String auth = "Basic " + Symm.base64.encode("testid:whatever");
+               resp = aaft.validate(LifeForm.CBLF, new Req("Authorization",auth), null);
+               assertEquals(TafResp.RESP.IS_AUTHENTICATED, resp.isAuthenticated());
+               
+       }
+//     @Test
+//     public void testRole() throws CadiException {
+//             TestAccess ta = new TestAccess();
+//             AAFLurRole1_0 aafLur = new AAFLurRole1_0(
+//                             ta,
+////                           "http://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=1.0.0/envContext=UAT/routeOffer=BAU_SE",
+//                             "http://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=1.0.0/envContext=DEV/routeOffer=D1",
+//                             "m12345", "m12345pass", 50000, // dme Time
+//                             // 5*60000); // 5 minutes User Expiration
+//                             50000, // 5 seconds after Expiration
+//                             200); // High Count of items.. These do not take much memory
+//
+//             Principal pri = new ConfigPrincipal("xy1234","whatever");
+//             for (int i = 0; i < 10; ++i) {
+////                   print(aafLur, pri, new LocalPermission("*|*|*|com.att.authz"));
+//                     print(aafLur, pri, new LocalPermission("service|myInstance|write"),false);
+//                     print(aafLur, pri, new LocalPermission("com.test.JU_Lur2_0Call.service|myInstance|write"),false);
+//                     print(aafLur, pri, new LocalPermission("org.osaaf.cadi"),true);
+//                     print(aafLur, pri, new LocalPermission("global"),true);
+//                     print(aafLur, pri, new LocalPermission("kumquat"),false);
+//             }
+//
+//             print(aafLur, pri, new LocalPermission("bogus"),false);
+//
+//             for (int i = 0; i < 10; ++i)
+//                     print(aafLur, pri, new LocalPermission("supergroup"),false);
+//
+//             System.out.println("All Done");
+//     }
+
+
+       private void print(Lur aafLur, Principal pri, Permission perm, boolean shouldBe)
+                       throws CadiException {
+               long start = System.nanoTime();
+       
+               // The Call
+               boolean ok = aafLur.fish(pri, perm);
+       
+               assertEquals(shouldBe,ok);
+               float ms = (System.nanoTime() - start) / 1000000f;
+               if (ok) {
+                       System.out.println("Yes, part of " + perm.getKey() + " (" + ms
+                                       + "ms)");
+               } else {
+                       System.out.println("No, not part of " + perm.getKey() + " (" + ms
+                                       + "ms)");
+               }
+       }
+
+       @SuppressWarnings("rawtypes")
+       public class Req implements HttpServletRequest {
+               private String[] headers;
+
+               public Req(String ... headers) {
+                       this.headers = headers;
+               }
+
+               public Object getAttribute(String name) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Enumeration getAttributeNames() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getCharacterEncoding() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public void setCharacterEncoding(String env)
+                               throws UnsupportedEncodingException {
+                       // TODO Auto-generated method stub
+                       
+               }
+
+               public int getContentLength() {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public String getContentType() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public ServletInputStream getInputStream() throws IOException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getParameter(String name) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Enumeration getParameterNames() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String[] getParameterValues(String name) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Map getParameterMap() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getProtocol() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getScheme() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getServerName() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public int getServerPort() {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public BufferedReader getReader() throws IOException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRemoteAddr() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRemoteHost() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public void setAttribute(String name, Object o) {
+                       // TODO Auto-generated method stub
+                       
+               }
+
+               public void removeAttribute(String name) {
+                       // TODO Auto-generated method stub
+                       
+               }
+
+               public Locale getLocale() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Enumeration getLocales() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public boolean isSecure() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               public RequestDispatcher getRequestDispatcher(String path) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRealPath(String path) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public int getRemotePort() {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public String getLocalName() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getLocalAddr() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public int getLocalPort() {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public String getAuthType() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public Cookie[] getCookies() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public long getDateHeader(String name) {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public String getHeader(String name) {
+                       for(int i=1;i<headers.length;i=i+2) {
+                               if(headers[i-1].equals(name)) return headers[i];
+                       }
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Enumeration getHeaders(String name) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @SuppressWarnings("unchecked")
+               public Enumeration getHeaderNames() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public int getIntHeader(String name) {
+                       // TODO Auto-generated method stub
+                       return 0;
+               }
+
+               public String getMethod() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getPathInfo() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getPathTranslated() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getContextPath() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getQueryString() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRemoteUser() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public boolean isUserInRole(String role) {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               public Principal getUserPrincipal() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRequestedSessionId() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getRequestURI() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public StringBuffer getRequestURL() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getServletPath() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public HttpSession getSession(boolean create) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public HttpSession getSession() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public boolean isRequestedSessionIdValid() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               public boolean isRequestedSessionIdFromCookie() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               public boolean isRequestedSessionIdFromURL() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               public boolean isRequestedSessionIdFromUrl() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               @Override
+               public ServletContext getServletContext() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public AsyncContext startAsync() throws IllegalStateException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public AsyncContext startAsync(ServletRequest servletRequest,
+                               ServletResponse servletResponse) throws IllegalStateException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public boolean isAsyncStarted() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               @Override
+               public boolean isAsyncSupported() {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               @Override
+               public AsyncContext getAsyncContext() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public DispatcherType getDispatcherType() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public boolean authenticate(HttpServletResponse response)
+                               throws IOException, ServletException {
+                       // TODO Auto-generated method stub
+                       return false;
+               }
+
+               @Override
+               public void login(String username, String password)
+                               throws ServletException {
+                       // TODO Auto-generated method stub
+                       
+               }
+
+               @Override
+               public void logout() throws ServletException {
+                       // TODO Auto-generated method stub
+                       
+               }
+
+               @Override
+               public Collection<Part> getParts() throws IOException, ServletException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               @Override
+               public Part getPart(String name) throws IOException, ServletException {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+               
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java
new file mode 100644 (file)
index 0000000..46c1064
--- /dev/null
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.lur.aaf.test;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+import org.onap.aaf.stillNeed.TestPrincipal;
+
+public class JU_MultiThreadPermHit {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               PropAccess myAccess = new PropAccess(); // 
+               
+               // 
+               try {
+                       AAFConHttp con = new AAFConHttp(myAccess,new PropertyLocator("https://mithrilcsp.sbc.com:8100"));
+                       
+                       // AAFLur has pool of DME clients as needed, and Caches Client lookups
+                       final AAFLurPerm aafLur = con.newLur();
+                       aafLur.setDebug("m12345@aaf.att.com");
+
+                       // Note: If you need both Authn and Authz construct the following:
+                       AAFAuthn<?> aafAuthn = con.newAuthn(aafLur);
+                       
+                       // Do not set Mech ID until after you construct AAFAuthn,
+                       // because we initiate  "401" info to determine the Realm of 
+                       // of the service we're after.
+                       final String id = myAccess.getProperty(Config.AAF_APPID,null);
+                       final String pass = myAccess.decrypt(myAccess.getProperty(Config.AAF_APPPASS,null),false);
+                       if(id!=null && pass!=null) {
+                               try {
+                                       
+                                       // Normally, you obtain Principal from Authentication System.
+       //                              // For J2EE, you can ask the HttpServletRequest for getUserPrincipal()
+       //                              // If you use CADI as Authenticator, it will get you these Principals from
+       //                              // CSP or BasicAuth mechanisms.
+       //                              String id = "cluster_admin@gridcore.att.com";
+       //
+       //                              // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason.
+                                       String ok;
+                                       ok = aafAuthn.validate(id, pass);
+                                       if(ok!=null) {
+                                               System.out.println(ok);
+                                       }
+
+                                       List<Permission> pond = new ArrayList<Permission>();
+                                       for(int i=0;i<20;++i) {
+                                               pond.clear();
+                                               Principal p = new TestPrincipal(i+id);
+                                               aafLur.fishAll(p, pond);
+                                               if(ok!=null && i%1000==0) {
+                                                       System.out.println(i + " " + ok);
+                                               }
+                                       }
+
+                                       for(int i=0;i<1000000;++i) {
+                                               ok = aafAuthn.validate( i+ id, "wrongPass");
+                                               if(ok!=null && i%1000==0) {
+                                                       System.out.println(i + " " + ok);
+                                               }
+                                       }
+       
+                                       final AAFPermission perm = new AAFPermission("org.osaaf.aaf.access","*","*");
+                                       
+                                       // Now you can ask the LUR (Local Representative of the User Repository about Authorization
+                                       // With CADI, in J2EE, you can call isUserInRole("org.osaaf.mygroup|mytype|write") on the Request Object 
+                                       // instead of creating your own LUR
+                                       for(int i=0;i<4;++i) {
+                                               Principal p = new TestPrincipal(i+id);
+
+                                               if(aafLur.fish(p, perm)) {
+                                                       System.out.println("Yes, " + id + " has permission for " + perm.getKey());
+                                               } else {
+                                                       System.out.println("No, " + id + " does not have permission for " + perm.getKey());
+                                               }
+                                       }
+       
+       
+                                       // Or you can all for all the Permissions available
+                                       List<Permission> perms = new ArrayList<Permission>();
+       
+                                       Principal p = new TestPrincipal(id);
+                                       aafLur.fishAll(p,perms);
+                                       System.out.println("Perms for " + id);
+                                       for(Permission prm : perms) {
+                                               System.out.println(prm.getKey());
+                                       }
+                                       
+                                       System.out.println("Press any key to continue");
+                                       System.in.read();
+                                       
+                                       for(int j=0;j<5;++j) {
+                                               new Thread(new Runnable() {
+                                                       @Override
+                                                       public void run() {
+                                                               for(int i=0;i<20;++i) {
+                                                                       Principal p = new TestPrincipal(id);
+                                                                       if(aafLur.fish(p, perm)) {
+                                                                               System.out.println("Yes, " + id + " has permission for " + perm.getKey());
+                                                                       } else {
+                                                                               System.out.println("No, " + id + " does not have permission for " + perm.getKey());
+                                                                       }
+                                                               }
+                                                       }
+                                               }).start();
+                                       }
+       
+                                       
+                               } finally {
+                                       aafLur.destroy();
+                               }
+                       } else { // checked on IDs
+                               System.err.println(Config.AAF_APPID + " and/or " + Config.AAF_APPPASS + " are not set.");
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_PermEval.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_PermEval.java
new file mode 100644 (file)
index 0000000..b95fbd3
--- /dev/null
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.lur.aaf.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.aaf.PermEval;
+
+public class JU_PermEval {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               assertTrue(PermEval.evalInstance(":com.att.temp:role:write",":!com.att.*:role:write"));
+               
+               // TRUE
+               assertTrue(PermEval.evalAction("fred","fred"));
+               assertTrue(PermEval.evalAction("fred,wilma","fred"));
+               assertTrue(PermEval.evalAction("barney,betty,fred,wilma","fred"));
+               assertTrue(PermEval.evalAction("*","fred"));
+               
+               assertTrue(PermEval.evalInstance("fred","fred"));
+               assertTrue(PermEval.evalInstance("fred,wilma","fred"));
+               assertTrue(PermEval.evalInstance("barney,betty,fred,wilma","fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               
+               assertTrue(PermEval.evalInstance(":fred:fred",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:fred,wilma",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:barney,betty,fred,wilma",":fred:fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               assertTrue(PermEval.evalInstance(":*:fred",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:*",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:fred",":!f.*:fred"));
+               assertTrue(PermEval.evalInstance(":fred:fred",":fred:!f.*"));
+               
+               /// FALSE
+               assertFalse(PermEval.evalInstance("fred","wilma"));
+               assertFalse(PermEval.evalInstance("fred,barney,betty","wilma"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":fred:wilma"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":wilma:fred"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":wilma:!f.*"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":!f.*:wilma"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":!w.*:!f.*"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":!f.*:!w.*"));
+
+               assertFalse(PermEval.evalInstance(":fred:fred",":fred:!x.*"));
+
+               // MSO Tests 12/3/2015
+               assertFalse(PermEval.evalInstance("/v1/services/features/*","/v1/services/features"));
+               assertFalse(PermEval.evalInstance(":v1:services:features:*",":v1:services:features"));
+               assertTrue(PermEval.evalInstance("/v1/services/features/*","/v1/services/features/api1"));
+               assertTrue(PermEval.evalInstance(":v1:services:features:*",":v1:services:features:api2"));
+               // MSO - Xue Gao
+               assertTrue(PermEval.evalInstance(":v1:requests:*",":v1:requests:test0-service"));   
+
+
+               
+               // Same tests, with Slashes
+               assertTrue(PermEval.evalInstance("/fred/fred","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/fred,wilma","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/barney,betty,fred,wilma","/fred/fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               assertTrue(PermEval.evalInstance("/*/fred","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/*","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/fred","/!f.*/fred"));
+               assertTrue(PermEval.evalInstance("/fred/fred","/fred/!f.*"));
+               
+               /// FALSE
+               assertFalse(PermEval.evalInstance("fred","wilma"));
+               assertFalse(PermEval.evalInstance("fred,barney,betty","wilma"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/fred/wilma"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/wilma/fred"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/wilma/!f.*"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/!f.*/wilma"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/!w.*/!f.*"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/!f.*/!w.*"));
+
+               assertFalse(PermEval.evalInstance("/fred/fred","/fred/!x.*"));
+               
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_TestAccess.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_TestAccess.java
new file mode 100644 (file)
index 0000000..1d19a42
--- /dev/null
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.lur.aaf.test;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.config.Config;
+
+public class JU_TestAccess implements Access {
+       private Symm symm;
+       private PrintStream out;
+
+       public JU_TestAccess(PrintStream out) {
+               this.out = out;
+               InputStream is = ClassLoader.getSystemResourceAsStream("cadi.properties");
+               try {
+                       System.getProperties().load(is);
+               } catch (IOException e) {
+                       e.printStackTrace(out);
+               } finally {
+                       try {
+                               is.close();
+                       } catch (IOException e) {
+                               e.printStackTrace(out);
+                       }
+               }
+               
+               String keyfile = System.getProperty(Config.CADI_KEYFILE);
+               if(keyfile==null) {
+                       System.err.println("No " + Config.CADI_KEYFILE + " in Classpath");
+               } else {
+                       try {
+                               is = new FileInputStream(keyfile);
+                               try {
+                                       symm = Symm.obtain(is);
+                               } finally {
+                                       is.close();
+                               }
+                       } catch (IOException e) {
+                               e.printStackTrace(out);
+                       }
+               }
+               
+
+
+       }
+       
+       public void log(Level level, Object... elements) {
+               boolean first = true;
+               for(int i=0;i<elements.length;++i) {
+                       if(first)first = false;
+                       else out.print(' ');
+                       out.print(elements[i].toString());
+               }
+               out.println();
+       }
+
+       public void log(Exception e, Object... elements) {
+               e.printStackTrace(out);
+               log(Level.ERROR,elements);
+       }
+
+       public void setLogLevel(Level level) {
+               
+       }
+
+       @Override
+       public boolean willLog(Level level) {
+               return true;
+       }
+
+       public ClassLoader classLoader() {
+               return ClassLoader.getSystemClassLoader();
+       }
+
+       public String getProperty(String string, String def) {
+               String rv = System.getProperty(string);
+               return rv==null?def:rv;
+       }
+
+       @Override
+       public Properties getProperties() {
+               return System.getProperties();
+       }
+
+       public void load(InputStream is) throws IOException {
+               
+       }
+
+       public String decrypt(String encrypted, boolean anytext) throws IOException {
+               return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC)))
+                       ? symm.depass(encrypted)
+                       : encrypted;
+       }
+
+       @Override
+       public void printf(Level level, String fmt, Object... elements) {
+               // TODO Auto-generated method stub
+               
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/JU_PermEval.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/JU_PermEval.java
new file mode 100644 (file)
index 0000000..f499986
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur.aaf.test1;
+
+import static org.junit.Assert.*;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.aaf.PermEval;
+
+public class JU_PermEval {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               // TRUE
+               assertTrue(PermEval.evalAction("fred","fred"));
+               assertTrue(PermEval.evalAction("fred,wilma","fred"));
+               assertTrue(PermEval.evalAction("barney,betty,fred,wilma","fred"));
+               assertTrue(PermEval.evalAction("*","fred"));
+               
+               assertTrue(PermEval.evalInstance("fred","fred"));
+               assertTrue(PermEval.evalInstance("fred,wilma","fred"));
+               assertTrue(PermEval.evalInstance("barney,betty,fred,wilma","fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               
+               assertTrue(PermEval.evalInstance(":fred:fred",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:fred,wilma",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:barney,betty,fred,wilma",":fred:fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               assertTrue(PermEval.evalInstance(":*:fred",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:*",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":!f.*:fred",":fred:fred"));
+               assertTrue(PermEval.evalInstance(":fred:!f.*",":fred:fred"));
+               
+               /// FALSE
+               assertFalse(PermEval.evalInstance("fred","wilma"));
+               assertFalse(PermEval.evalInstance("fred,barney,betty","wilma"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":fred:wilma"));
+               assertFalse(PermEval.evalInstance(":fred:fred",":wilma:fred"));
+               assertFalse(PermEval.evalInstance(":wilma:!f.*",":fred:fred"));
+               assertFalse(PermEval.evalInstance(":!f.*:wilma",":fred:fred"));
+               assertFalse(PermEval.evalInstance(":!w.*:!f.*",":fred:fred"));
+               assertFalse(PermEval.evalInstance(":!f.*:!w.*",":fred:fred"));
+
+               assertFalse(PermEval.evalInstance(":fred:!x.*",":fred:fred"));
+
+               // MSO Tests 12/3/2015
+               assertFalse(PermEval.evalInstance("/v1/services/features/*","/v1/services/features"));
+               assertFalse(PermEval.evalInstance(":v1:services:features:*",":v1:services:features"));
+               assertTrue(PermEval.evalInstance("/v1/services/features/*","/v1/services/features/api1"));
+               assertTrue(PermEval.evalInstance(":v1:services:features:*",":v1:services:features:api2"));
+               // MSO - Xue Gao
+               assertTrue(PermEval.evalInstance(":v1:requests:*",":v1:requests:test0-service"));   
+
+
+               
+               // Same tests, with Slashes
+               assertTrue(PermEval.evalInstance("/fred/fred","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/fred,wilma","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/barney,betty,fred,wilma","/fred/fred"));
+               assertTrue(PermEval.evalInstance("*","fred"));
+               assertTrue(PermEval.evalInstance("/*/fred","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/*","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/!f.*/fred","/fred/fred"));
+               assertTrue(PermEval.evalInstance("/fred/!f.*","/fred/fred"));
+               
+               /// FALSE
+               assertFalse(PermEval.evalInstance("fred","wilma"));
+               assertFalse(PermEval.evalInstance("fred,barney,betty","wilma"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/fred/wilma"));
+               assertFalse(PermEval.evalInstance("/fred/fred","/wilma/fred"));
+               assertFalse(PermEval.evalInstance("/wilma/!f.*","/fred/fred"));
+               assertFalse(PermEval.evalInstance("/!f.*/wilma","/fred/fred"));
+               assertFalse(PermEval.evalInstance("/!w.*/!f.*","/fred/fred"));
+               assertFalse(PermEval.evalInstance("/!f.*/!w.*","/fred/fred"));
+
+               assertFalse(PermEval.evalInstance("/fred/!x.*","/fred/fred"));
+               
+               assertTrue(PermEval.evalInstance(":!com.att.*:role:write",":com.att.temp:role:write"));
+
+               // CPFSF-431 Group needed help with Wild Card
+               // They tried
+               assertTrue(PermEval.evalInstance(
+                               ":topic.com.att.ecomp_test.crm.pre*",
+                               ":topic.com.att.ecomp_test.crm.predemo100"
+                               ));
+
+               // Also can be
+               assertTrue(PermEval.evalInstance(
+                               ":!topic.com.att.ecomp_test.crm.pre.*",
+                               ":topic.com.att.ecomp_test.crm.predemo100"
+                               ));
+
+               
+
+
+               
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java
new file mode 100644 (file)
index 0000000..229f41a
--- /dev/null
@@ -0,0 +1,145 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur.aaf.test1;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+import org.onap.aaf.stillNeed.TestPrincipal;
+
+public class MultiThreadPermHit {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               PropAccess myAccess = new PropAccess(); // 
+               
+               // 
+               try {
+                       AAFConHttp con = new AAFConHttp(myAccess,new PropertyLocator("https://mithrilcsp.sbc.com:8100"));
+                       
+                       // AAFLur has pool of DME clients as needed, and Caches Client lookups
+                       final AAFLurPerm aafLur = con.newLur();
+                       aafLur.setDebug("m12345@aaf.att.com");
+
+                       // Note: If you need both Authn and Authz construct the following:
+                       AAFAuthn<?> aafAuthn = con.newAuthn(aafLur);
+                       
+                       // Do not set Mech ID until after you construct AAFAuthn,
+                       // because we initiate  "401" info to determine the Realm of 
+                       // of the service we're after.
+                       final String id = myAccess.getProperty(Config.AAF_APPID,null);
+                       final String pass = myAccess.decrypt(myAccess.getProperty(Config.AAF_APPPASS,null),false);
+                       if(id!=null && pass!=null) {
+                               try {
+                                       
+                                       // Normally, you obtain Principal from Authentication System.
+       //                              // For J2EE, you can ask the HttpServletRequest for getUserPrincipal()
+       //                              // If you use CADI as Authenticator, it will get you these Principals from
+       //                              // CSP or BasicAuth mechanisms.
+       //                              String id = "cluster_admin@gridcore.att.com";
+       //
+       //                              // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason.
+                                       String ok;
+                                       ok = aafAuthn.validate(id, pass,null /* use AuthzTrans or HttpServlet, if you have it */);
+                                       if(ok!=null) {
+                                               System.out.println(ok);
+                                       }
+
+                                       List<Permission> pond = new ArrayList<Permission>();
+                                       for(int i=0;i<20;++i) {
+                                               pond.clear();
+                                               aafLur.fishAll(new TestPrincipal(i+id), pond);
+                                               if(ok!=null && i%1000==0) {
+                                                       System.out.println(i + " " + ok);
+                                               }
+                                       }
+
+                                       for(int i=0;i<1000000;++i) {
+                                               ok = aafAuthn.validate( i+ id, "wrongPass",null /* use AuthzTrans or HttpServlet, if you have it */);
+                                               if(ok!=null && i%1000==0) {
+                                                       System.out.println(i + " " + ok);
+                                               }
+                                       }
+       
+                                       final AAFPermission perm = new AAFPermission("org.osaaf.aaf.access","*","*");
+                                       
+                                       // Now you can ask the LUR (Local Representative of the User Repository about Authorization
+                                       // With CADI, in J2EE, you can call isUserInRole("org.osaaf.mygroup|mytype|write") on the Request Object 
+                                       // instead of creating your own LUR
+                                       final Principal p = new TestPrincipal(id);
+                                       for(int i=0;i<4;++i) {
+                                               if(aafLur.fish(p, perm)) {
+                                                       System.out.println("Yes, " + id + " has permission for " + perm.getKey());
+                                               } else {
+                                                       System.out.println("No, " + id + " does not have permission for " + perm.getKey());
+                                               }
+                                       }
+       
+       
+                                       // Or you can all for all the Permissions available
+                                       List<Permission> perms = new ArrayList<Permission>();
+       
+                                       
+                                       aafLur.fishAll(p,perms);
+                                       System.out.println("Perms for " + id);
+                                       for(Permission prm : perms) {
+                                               System.out.println(prm.getKey());
+                                       }
+                                       
+                                       System.out.println("Press any key to continue");
+                                       System.in.read();
+                                       
+                                       for(int j=0;j<5;++j) {
+                                               new Thread(new Runnable() {
+                                                       @Override
+                                                       public void run() {
+                                                               for(int i=0;i<20;++i) {
+                                                                       if(aafLur.fish(p, perm)) {
+                                                                               System.out.println("Yes, " + id + " has permission for " + perm.getKey());
+                                                                       } else {
+                                                                               System.out.println("No, " + id + " does not have permission for " + perm.getKey());
+                                                                       }
+                                                               }
+                                                       }
+                                               }).start();
+                                       }
+       
+                                       
+                               } finally {
+                                       aafLur.destroy();
+                               }
+                       } else { // checked on IDs
+                               System.err.println(Config.AAF_APPID + " and/or " + Config.AAF_APPPASS + " are not set.");
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_FastPerms.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_FastPerms.java
new file mode 100644 (file)
index 0000000..523bbc5
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth.test;
+
+import java.io.StringReader;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.oauth.TokenPerm.LoadPermissions;
+import org.onap.aaf.misc.rosetta.ParseException;
+
+public class JU_FastPerms {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               String json = "{\"perm\":[" +
+                               "  {\"type\":\"com.access\",\"instance\":\"*\",\"action\":\"read,approve\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.attrib\",\"instance\":\":com.att.*:swm\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.bogus\",\"instance\":\"sample\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.ca\",\"instance\":\"aaf\",\"action\":\"ip\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.ca\",\"instance\":\"local\",\"action\":\"domain\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.cache\",\"instance\":\"*\",\"action\":\"clear\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.cass\",\"instance\":\":mithril\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.certman\",\"instance\":\"local\",\"action\":\"read,request,showpass\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.db\",\"instance\":\"pool\",\"action\":\"clear\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.deny\",\"instance\":\"com.att\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.jenkins\",\"instance\":\"mithrilcsp.sbc.com\",\"action\":\"admin\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.log\",\"instance\":\"com.att\",\"action\":\"id\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.myPerm\",\"instance\":\"myInstance\",\"action\":\"myAction\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.ns\",\"instance\":\":com.att.*:ns\",\"action\":\"write\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.ns\",\"instance\":\":com.att:ns\",\"action\":\"write\"}," +
+                               "  {\"type\":\"org.osaaf.aaf.password\",\"instance\":\"com.att\",\"action\":\"extend\"}," +
+                               "  {\"type\":\"org.osaaf.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.authz.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.authz.dev.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.authz.swm.star\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.cadi.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.chris.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.csid.lab.swm.node\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.myapp.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.myapp.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.sample.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.sample.swm.myPerm\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.temp.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"org.osaaf.test.access\",\"instance\":\"*\",\"action\":\"*\"}," +
+                               "  {\"type\":\"org.osaaf.test.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"com.test.access\",\"instance\":\"*\",\"action\":\"read\"}," +
+                               "  {\"type\":\"com.test.access\",\"instance\":\"*\",\"action\":\"read\"}" +
+                               "]}";
+               
+               try {
+                       LoadPermissions lp = new LoadPermissions(new StringReader(json));
+                       for(Permission p : lp.perms) {
+                               System.out.println(p.toString());
+                       }
+                       System.out.println("done");
+               } catch (ParseException e) {
+                       System.err.println(e);
+               }
+               
+               
+       }
+       
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java
new file mode 100644 (file)
index 0000000..cc458f2
--- /dev/null
@@ -0,0 +1,277 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth.test;
+
+import java.net.ConnectException;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.oauth.TimedToken;
+import org.onap.aaf.cadi.oauth.TokenClient;
+import org.onap.aaf.cadi.oauth.TokenClientFactory;
+import org.onap.aaf.cadi.oauth.TzClient;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+
+import aafoauth.v2_0.Introspect;
+import aafoauth.v2_0.Token;
+import junit.framework.Assert;
+
+public class JU_OAuthTest {
+
+       private static PropAccess access;
+       private static TokenClientFactory tcf;
+
+       @BeforeClass
+       public static void setUpBeforeClass()  {
+               access = new PropAccess();
+               try {
+                       tcf = TokenClientFactory.instance(access);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       Assert.fail();
+               }
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void testROPCFlowHappy() {
+               try {
+                       // AAF OAuth
+                       String client_id = access.getProperty(Config.AAF_APPID);
+                       String client_secret = access.getProperty(Config.AAF_APPPASS);
+                       String tokenServiceURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL);
+                       Assert.assertNotNull(tokenServiceURL);
+                       String tokenIntrospectURL = access.getProperty(Config.AAF_OAUTH2_INTROSPECT_URL);
+                       String tokenAltIntrospectURL = access.getProperty(Config.AAF_ALT_OAUTH2_INTROSPECT_URL);
+                       Assert.assertNotNull(tokenIntrospectURL);
+                       final String endServicesURL = access.getProperty(Config.AAF_OAUTH2_HELLO_URL);
+                       String username = access.getProperty("cadi_username");
+
+                       TokenClient tc;
+                       Result<TimedToken> rtt;
+                       if(true) {
+                               tc = tcf.newClient(tokenServiceURL, 3000);
+                               tc.client_creds(client_id,client_secret);
+                               tc.password(access.getProperty("cadi_username"),access.getProperty("cadi_password"));
+                               rtt = tc.getToken(Kind.BASIC_AUTH,"org.osaaf.aaf","org.osaaf.test");
+                               if(rtt.isOK()) {
+                                       print(rtt.value);
+                                       rtt = tc.refreshToken(rtt.value);
+                                       if(rtt.isOK()) {
+                                               print(rtt.value);
+                                               TokenClient ic = tcf.newClient(tokenIntrospectURL,3000);
+                                               ic.client_creds(client_id,client_secret);
+
+                                               Result<Introspect> ri = ic.introspect(rtt.value.getAccessToken());
+                                               if(ri.isOK()) {
+                                                       print(ri.value);
+                                               } else {
+                                                       System.out.println(ri.code + ' ' + ri.error);
+                                                       Assert.fail(ri.code + ' ' + ri.error);
+                                               }
+                                               TzClient helloClient = tcf.newTzClient(endServicesURL);
+                                               helloClient.setToken(client_id, rtt.value);
+//                                             String rv = serviceCall(helloClient);
+//                                             System.out.println(rv);
+               //                              Assert.assertEquals("Hello AAF OAuth2\n",rv);
+                                       } else {
+                                               System.out.println(rtt.code + ' ' + rtt.error);
+                                               Assert.fail(rtt.code + ' ' + rtt.error);
+                                       }
+                               } else {
+                                       System.out.println(rtt.code + ' ' + rtt.error);
+                                       Assert.fail(rtt.code + ' ' + rtt.error);
+                               }
+                       }
+               
+                       // ISAM Test
+                       if(true) {
+                               System.out.println("**** ISAM TEST ****");
+                               tokenServiceURL=access.getProperty(Config.AAF_ALT_OAUTH2_TOKEN_URL);
+                               client_id=access.getProperty(Config.AAF_ALT_CLIENT_ID);
+                               client_secret=access.getProperty(Config.AAF_ALT_CLIENT_SECRET);
+                               if(tokenServiceURL!=null) {
+                                       tc = tcf.newClient(tokenServiceURL, 3000);
+                                       tc.client_creds(client_id, client_secret);
+                                       int at = username.indexOf('@');
+                                       
+                                       tc.password(at>=0?username.substring(0, at):username,access.getProperty("cadi_password"));
+                                       rtt = tc.getToken("org.osaaf.aaf","org.osaaf.test");
+                                       if(rtt.isOK()) {
+                                               print(rtt.value);
+                                               rtt = tc.refreshToken(rtt.value);
+                                               if(rtt.isOK()) {
+                                                       print(rtt.value);
+                                                       
+                                                       tc = tcf.newClient(tokenAltIntrospectURL, 3000);
+                                                       tc.client_creds(client_id, client_secret);
+                                                       Result<Introspect> rti = tc.introspect(rtt.value.getAccessToken());
+                                                       if(rti.isOK()) {
+                                                               System.out.print("Normal ISAM ");
+                                                               print(rti.value);
+                                                       } else {
+                                                               System.out.println(rti.code + ' ' + rti.error);
+                                                               Assert.fail(rtt.code + ' ' + rtt.error);
+                                                       }
+
+                                                       tc = tcf.newClient(tokenIntrospectURL, 3000);
+                                                       tc.client_creds(client_id, client_secret);
+                                                       rti = tc.introspect(rtt.value.getAccessToken());
+                                                       if(rti.isOK()) {
+                                                               System.out.print("AAF with ISAM Token ");
+                                                               print(rti.value);
+                                                       } else {
+                                                               System.out.println(rti.code + ' ' + rti.error);
+                                                               if(rti.code!=404) {
+                                                                       Assert.fail(rti.code + ' ' + rti.error);
+                                                               }
+                                                       }
+
+                                                       TzClient tzClient = tcf.newTzClient(endServicesURL);
+                                                       tzClient.setToken(client_id, rtt.value);
+                                                       // Note: this is AAF's "Hello" server
+                                                       String rv = serviceCall(tzClient);
+                                                       System.out.println(rv);
+                       //                              Assert.assertEquals("Hello AAF OAuth2\n",rv);
+                                               } else {
+                                                       System.out.println(rtt.code + ' ' + rtt.error);
+                                                       Assert.fail(rtt.code + ' ' + rtt.error);
+                                               }
+                                       } else {
+                                               System.out.println(rtt.code + ' ' + rtt.error);
+                                               Assert.fail(rtt.code + ' ' + rtt.error);
+                                       }
+                               } else {
+                                       Assert.fail(Config.AAF_ALT_OAUTH2_TOKEN_URL + " is required");
+                               }
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       Assert.fail();
+               }
+       }
+       
+       
+       private TokenClient testROPCFlow(final String url, final String client_id, final String client_secret, String user, String password, final String ... scope) throws Exception {
+               TokenClient tclient = tcf.newClient(url,3000);
+               tclient.client_creds(client_id, client_secret);
+               if(user!=null && password!=null) {
+                       tclient.password(user,password);
+               }
+               Result<TimedToken> rt = tclient.getToken(scope);
+               if(rt.isOK()) {
+                       print(rt.value);
+                       Result<Introspect> rti = tclient.introspect(rt.value.getAccessToken());
+                       if(rti.isOK()) {
+                               print(rti.value);
+                       } else {
+                               printAndFail(rti);
+                       }
+               } else {
+                       printAndFail(rt);
+               }
+               return tclient;
+       }
+       
+       private String serviceCall(TzClient tzClient) throws Exception {
+               return tzClient.best(new Retryable<String>() {
+                       @Override
+                       public String code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                               Future<String> future = client.read(null,"text/plain");
+                               if(future.get(3000)) {
+                                       return future.value;
+                               } else {
+                                       throw new APIException(future.code()  + future.body());
+                               }
+                       }
+               });
+       }
+       private void printAndFail(Result<?> rt) {
+               System.out.printf("HTTP Code %d: %s\n", rt.code, rt.error);
+               Assert.fail(rt.toString());
+       }
+
+       private void print(Token t) {
+               GregorianCalendar exp_date = new GregorianCalendar();
+               exp_date.add(GregorianCalendar.SECOND, t.getExpiresIn());
+               System.out.printf("Access Token\n\tToken:\t\t%s\n\tToken Type:\t%s\n\tExpires In:\t%d (%s)\n\tScope:\t\t%s\n\tRefresh Token:\t%s\n",
+               t.getAccessToken(),
+               t.getTokenType(),
+               t.getExpiresIn(),
+               Chrono.timeStamp(new Date(System.currentTimeMillis()+(t.getExpiresIn()*1000))),
+               t.getScope(),
+               t.getRefreshToken());
+       }
+       
+       private void print(Introspect ti) {
+               if(ti==null || ti.getClientId()==null) {
+                       System.out.println("Empty Introspect");
+                       return;
+               }
+               Date exp = new Date(ti.getExp()*1000); // seconds
+               System.out.printf("Introspect\n"
+                               + "\tAccessToken:\t%s\n"
+                               + "\tClient-id:\t%s\n"
+                               + "\tClient Type:\t%s\n"
+                               + "\tActive:  \t%s\n"
+                               + "\tUserName:\t%s\n"
+                               + "\tExpires: \t%d (%s)\n"
+                               + "\tScope:\t\t%s\n"
+                               + "\tContent:\t\t%s\n",
+               ti.getAccessToken(),
+               ti.getClientId(),
+               ti.getClientType(),
+               ti.isActive()?Boolean.TRUE.toString():Boolean.FALSE.toString(),
+               ti.getUsername(),
+               ti.getExp(),
+               Chrono.timeStamp(exp),
+               ti.getScope(),
+               ti.getContent()==null?"":ti.getContent());
+               
+               System.out.println();
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthToken.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthToken.java
new file mode 100644 (file)
index 0000000..0ddb3f4
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.oauth.test;
+
+import static org.junit.Assert.*;
+
+import java.util.UUID;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.oauth.AAFToken;
+
+import junit.framework.Assert;
+
+public class JU_OAuthToken {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void testMax() throws CadiException {
+               UUID uuid = new UUID(Long.MAX_VALUE,Long.MAX_VALUE);
+               String token = AAFToken.toToken(uuid);
+               System.out.println(token);
+               UUID uuid2 = AAFToken.fromToken(token);
+               Assert.assertEquals(uuid, uuid2);
+       }
+       
+       @Test
+       public void testMin() throws CadiException {
+               UUID uuid = new UUID(Long.MIN_VALUE,Long.MIN_VALUE);
+               String token = AAFToken.toToken(uuid);
+               System.out.println(token);
+               UUID uuid2 = AAFToken.fromToken(token);
+               Assert.assertEquals(uuid, uuid2);
+       }
+
+       @Test
+       public void testRandom() throws CadiException {
+               for(int i=0;i<100;++i) {
+                       UUID uuid = UUID.randomUUID();
+                       String token = AAFToken.toToken(uuid);
+                       System.out.println(token);
+                       UUID uuid2 = AAFToken.fromToken(token);
+                       Assert.assertEquals(uuid, uuid2);
+               }
+       }
+
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java b/cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java
new file mode 100644 (file)
index 0000000..21012e6
--- /dev/null
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.content;
+
+import java.io.StringReader;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaData;
+import org.onap.aaf.misc.rosetta.env.RosettaEnv;
+
+import aaf.v2_0.Error;
+
+public class JU_Content {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+
+       @Test
+       public void parseErrorJSON() throws Exception {
+               final String msg = "{\"messageId\":\"SVC2000\",\"text\":\"Select which cred to delete (or 0 to delete all):" +
+                       "1) %1" +
+                       "2) %2" +
+                       "3) %3" +
+                       "4) %4" +
+                       "Run same command again with chosen entry as last parameter\"," +
+                       "\"variables\":[" +
+                       "\"m55555@jr583u.cred.test.com 1 Wed Oct 08 11:48:08 CDT 2014\"," +
+                       "\"m55555@jr583u.cred.test.com 1 Thu Oct 09 12:54:46 CDT 2014\"," +
+                       "\"m55555@jr583u.cred.test.com 1 Tue Jan 06 05:00:00 CST 2015\"," +
+                       "\"m55555@jr583u.cred.test.com 1 Wed Jan 07 05:00:00 CST 2015\"]}";
+               
+               Error err = new Error();
+               err.setText("Hello");
+               err.getVariables().add("I'm a teapot");
+               err.setMessageId("12");
+               
+               
+//             System.out.println(msg);
+               RosettaEnv env = new RosettaEnv();
+               RosettaDF<aaf.v2_0.Error> errDF = env.newDataFactory(aaf.v2_0.Error.class);
+               errDF.in(RosettaData.TYPE.JSON);
+               errDF.out(RosettaData.TYPE.JSON);
+               RosettaData<Error> data = errDF.newData();
+               data.load(err);
+               System.out.println(data.asString());
+               
+               data.load(new StringReader(msg));
+               err = data.asObject();
+               System.out.println(err.getText());
+       }
+               
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java
new file mode 100644 (file)
index 0000000..387c4d1
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.example;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.locator.DNSLocator;
+
+public class JU_ExampleAuthCheck {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               PropAccess myAccess = new PropAccess(); // 
+               
+               try {
+                       AAFConHttp acon = new AAFConHttp(myAccess, new DNSLocator(
+                                       myAccess,"https","localhost","8100"));
+                       AAFAuthn<?> authn = acon.newAuthn();
+                       long start; 
+                       for (int i=0;i<10;++i) {
+                               start = System.nanoTime();
+                               String err = authn.validate("", "gritty");
+                               if(err!=null) System.err.println(err);
+                               else System.out.println("I'm ok");
+                               
+                               err = authn.validate("bogus", "gritty");
+                               if(err!=null) System.err.println(err + " (correct error)");
+                               else System.out.println("I'm ok");
+
+                               System.out.println((System.nanoTime()-start)/1000000f + " ms");
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java
new file mode 100644 (file)
index 0000000..732ea81
--- /dev/null
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.example;
+
+import java.security.Principal;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.locator.DNSLocator;
+import org.onap.aaf.cadi.lur.LocalPermission;
+
+public class JU_X509Test {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               
+               PropAccess myAccess = new PropAccess();
+               
+               // 
+               try {
+                       AAFConHttp con = new AAFConHttp(myAccess, 
+                                       new DNSLocator(myAccess,"https","mithrilcsp.sbc.com","8100"));
+                       
+                       // AAFLur has pool of DME clients as needed, and Caches Client lookups
+                       AAFLurPerm aafLur = con.newLur();
+                       
+                       // Note: If you need both Authn and Authz construct the following:
+//                     AAFAuthn<?> aafAuthn = con.newAuthn(aafLur);
+                       
+                       // con.x509Alias("aaf.att"); // alias in keystore
+
+                       try {
+                               
+                               // Normally, you obtain Principal from Authentication System.
+//                             // For J2EE, you can ask the HttpServletRequest for getUserPrincipal()
+//                             // If you use CADI as Authenticator, it will get you these Principals from
+//                             // CSP or BasicAuth mechanisms.
+//                             String id = "cluster_admin@gridcore.att.com";
+//
+//                             // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason.
+                               Future<String> fs = 
+                                               con.client("2.0").read("/authz/perms/com.att.aaf.ca","application/Perms+json");
+                               if(fs.get(3000)) {
+                                       System.out.println(fs.value);
+                               } else {
+                                       System.out.println("Error: "  + fs.code() + ':' + fs.body());
+                               }
+                               
+                               // Check on Perms with LUR
+                               if(aafLur.fish(new Principal() {
+                                       @Override
+                                       public String getName() {
+                                               return "m12345@aaf.att.com";
+                                       }
+                               }, new LocalPermission("org.osaaf.aaf.ca|aaf|request"))) {
+                                       System.out.println("Has Perm");
+                               } else {
+                                       System.out.println("Does NOT Have Perm");
+                               }
+                       } finally {
+                               aafLur.destroy();
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java
new file mode 100644 (file)
index 0000000..960ea06
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.stillNeed;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.http.HBasicAuthSS;
+import org.onap.aaf.cadi.http.HClient;
+import org.onap.aaf.cadi.http.HX509SS;
+
+public class CadiTest {
+       public static void main(String args[]) {
+               Access access = new PropAccess();
+               try {
+                       SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class);
+                       SecuritySetter<HttpURLConnection> ss;
+                       if(access.getProperty(Config.CADI_ALIAS,null)!=null) {
+                               ss = new HX509SS(si);
+                       } else {
+                               ss = new HBasicAuthSS(si);
+                       }
+                       HClient hclient = new HClient(ss,new URI("https://zlp08851.vci.att.com:8095"),3000);
+                       hclient.setMethod("OPTIONS");
+                       hclient.setPathInfo("/cadi/log/set/WARN");
+                       hclient.send();
+                       Future<String> future = hclient.futureReadString();
+                       if(future.get(5000)) {
+                               System.out.printf("Success %s",future.value);
+                       } else {
+                               System.out.printf("Error: %d-%s", future.code(),future.body());
+                       }
+                               
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java
new file mode 100644 (file)
index 0000000..a4b1cf1
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.stillNeed;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.locator.DNSLocator;
+
+public class ExampleAuthCheck {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               PropAccess myAccess = new PropAccess(); // 
+               
+               try {
+                       AAFConHttp acon = new AAFConHttp(myAccess, new DNSLocator(
+                                       myAccess,"https","localhost","8100"));
+                       AAFAuthn<?> authn = acon.newAuthn();
+                       long start; 
+                       for (int i=0;i<10;++i) {
+                               start = System.nanoTime();
+                               String err = authn.validate("", "gritty",null);
+                               if(err!=null) System.err.println(err);
+                               else System.out.println("I'm ok");
+                               
+                               err = authn.validate("bogus", "gritty",null);
+                               if(err!=null) System.err.println(err + " (correct error)");
+                               else System.out.println("I'm ok");
+
+                               System.out.println((System.nanoTime()-start)/1000000f + " ms");
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+       }
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java
new file mode 100644 (file)
index 0000000..1256902
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.stillNeed;
+
+import java.security.Principal;
+
+public class TestPrincipal implements Principal {
+       private String name;
+       public TestPrincipal(String name) {
+               this.name = name;
+       }
+       @Override
+       public String getName() {
+               return name;
+       }
+
+}
diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java
new file mode 100644 (file)
index 0000000..290f573
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.stillNeed;
+
+import java.security.Principal;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.locator.DNSLocator;
+import org.onap.aaf.cadi.lur.LocalPermission;
+
+//TODO Needs running service to TEST
+
+public class X509Test {
+       public static void main(String args[]) {
+               // Link or reuse to your Logging mechanism
+               
+               PropAccess myAccess = new PropAccess();
+               
+               // 
+               try {
+                       AAFConHttp con = new AAFConHttp(myAccess, 
+                                       new DNSLocator(myAccess,"https","mithrilcsp.sbc.com","8100"));
+                       
+                       // AAFLur has pool of DME clients as needed, and Caches Client lookups
+                       AAFLurPerm aafLur = con.newLur();
+                       
+                       // Note: If you need both Authn and Authz construct the following:
+//                     AAFAuthn<?> aafAuthn = con.newAuthn(aafLur);
+                       
+                       // con.x509Alias("aaf.att"); // alias in keystore
+
+                       try {
+                               
+                               // Normally, you obtain Principal from Authentication System.
+//                             // For J2EE, you can ask the HttpServletRequest for getUserPrincipal()
+//                             // If you use CADI as Authenticator, it will get you these Principals from
+//                             // CSP or BasicAuth mechanisms.
+//                             String id = "cluster_admin@gridcore.att.com";
+//
+//                             // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason.
+                               Future<String> fs = 
+                                               con.client("2.0").read("/authz/perms/com.att.aaf.ca","application/Perms+json");
+                               if(fs.get(3000)) {
+                                       System.out.println(fs.value);
+                               } else {
+                                       System.out.println("Error: "  + fs.code() + ':' + fs.body());
+                               }
+                               
+                               // Check on Perms with LUR
+                               if(aafLur.fish(new Principal() {
+                                       @Override
+                                       public String getName() {
+                                               return "m12345@aaf.att.com";
+                                       }
+                               }, new LocalPermission("org.osaaf.aaf.ca|aaf|request"))) {
+                                       System.out.println("Has Perm");
+                               } else {
+                                       System.out.println("Does NOT Have Perm");
+                               }
+                       } finally {
+                               aafLur.destroy();
+                       }
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+       }
+}
diff --git a/cadi/aaf/src/test/resources/cadi.properties b/cadi/aaf/src/test/resources/cadi.properties
new file mode 100644 (file)
index 0000000..810df57
--- /dev/null
@@ -0,0 +1,37 @@
+##
+## AUTHZ API (authz-service) Properties
+##
+AFT_LATITUDE=32.780140
+AFT_LONGITUDE=-96.800451
+AFT_ENVIRONMENT=AFTUAT
+DEPLOYED_VERSION=2.0.MITHRIL
+cadi_prop_files=/opt/app/aaf/common/com.att.aaf.props
+
+#cadi_keystore=/Volumes/Data/src/authz/common/cadiaaf.jks
+#cadi_truststore=/Volumes/Data/src/authz/common/caditrust.jks
+#cadi_keystore_password=enc:4s9TVkWDpUhjgimeXEDL7fE7gaTvppkGwiU7arrtu504ol9uB51swkZkqW7qTr_T
+#cadi_key_password=enc:4s9TVkWDpUhjgimeXEDL7fE7gaTvppkGwiU7arrtu504ol9uB51swkZkqW7qTr_T
+#cadi_truststore_password=enc:HHFqU-eYs2653Ifsm4m-m4TkehxB13x4kZxQqsf-ydz
+# cadi_trust_all_x509=true
+#cadi_alias=aaf.att
+https.protocols=TLSv1.1,TLSv1.2
+
+# cm_url=https://mithrilcsp.sbc.com:8150
+
+#basic_realm=localized
+#basic_warn=false
+#localhost_deny=false
+
+#cass_group_name=com.att.aaf
+#cass_cluster_name=mithrilcsp.sbc.com
+#aaf_default_realm=com.att.csp
+
+#aaf_url=https://135.110.241.35:8100
+#aaf_url=https://mithrilcsp.sbc.com:8095/proxy
+aaf_url=https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=DEV/routeOffer=BAU_SE
+#aaf_id=m12345@aaf.att.com
+#aaf_password=enc:mH3t-3tBYXrf8RUhGP6unXH9z75Ba-kBDQTiAHblMju
+
+aaf_user_expires=3000
+aaf_clean_interval=4000
+
diff --git a/cadi/aaf/src/test/resources/log4j.properties b/cadi/aaf/src/test/resources/log4j.properties
new file mode 100644 (file)
index 0000000..5ec6388
--- /dev/null
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] %m %n
+
+# General Apache libraries
+log4j.rootLogger=WARN,stdout
+log4j.logger.org.apache=WARN,stdout
+log4j.logger.dme2=WARN,stdout
+log4j.logger.init=INFO,stdout
+log4j.logger.authz=INFO,stdout
+log4j.logger.audit=WARN,stdout
+
+
+
diff --git a/cadi/cass/.gitignore b/cadi/cass/.gitignore
new file mode 100644 (file)
index 0000000..2fd21f9
--- /dev/null
@@ -0,0 +1,4 @@
+/target/
+/.classpath
+/.settings/
+/target/
diff --git a/cadi/cass/.project b/cadi/cass/.project
new file mode 100644 (file)
index 0000000..8efd1f6
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>cadi_cass</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/cadi/cass/etc/cadi.properties b/cadi/cass/etc/cadi.properties
new file mode 100644 (file)
index 0000000..767fe5c
--- /dev/null
@@ -0,0 +1,45 @@
+##
+## AUTHZ API (authz-service) Properties
+##
+
+## DISCOVERY (DME2) Parameters on the Command Line
+AFT_LATITUDE=38.627345
+AFT_LONGITUDE=-90.193774
+AFT_ENVIRONMENT=AFTUAT
+
+# CADI 
+cadi_keyfile=/opt/app/aaf/common/com.att.aaf.keyfile
+cadi_loglevel=WARN
+
+# CASSANDRA Required for APP
+cass_group_name=com.att.aaf
+
+# CASSANDRA Optional
+cass_cluster_name=mithril
+
+# AAF Required for APP
+aaf_url=https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=DEV/routeOffer=BAU_SE
+DME2_EP_REGISTRY_CLASS=DME2FS
+AFT_DME2_EP_REGISTRY_FS_DIR=/Volumes/Data/src/authz/dme2reg
+
+aaf_default_realm=aaf.localized
+aaf_id=m12345@aaf.att.com
+aaf_password=enc:DuADW3_Y26NdHgDvN7Prt7vtDwYJM3Kvw9AOQ30lPV1
+cadi_loglevel=DEBUG
+
+# AAF Optional
+# Connection Time Out (milliseconds)
+aaf_conn_timeout=10000
+# User Cache Expiration (milliseconds)
+aaf_user_expires=600000
+# High count... Rough top number of objects held in Cache per cycle.  If high is reached, more are
+# recycled next time.  
+aaf_high_count=1000
+
+##
+## Localized Passwords
+##
+basic_realm=aaf.localized
+local_users=m01891@aaf.localized%DuADW3_Y26NdHgDvN7Prt7vtDwYJM3Kvw9AOQ30lPV1:/mithril/authz;\
+            m12345@aaf.localized%DuADW3_Y26NdHgDvN7Prt7vtDwYJM3Kvw9AOQ30lPV1:/mithril/authz;\
+            root@aaf.localized%DuADW3_Y26NdHgDvN7Prt7vtDwYJM3Kvw9AOQ30lPV1:/mithril
diff --git a/cadi/cass/pom.xml b/cadi/cass/pom.xml
new file mode 100644 (file)
index 0000000..9db57a4
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============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====================================================
+ *
+-->
+<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.aaf.cadi</groupId>
+               <artifactId>parent</artifactId>
+               <version>1.5.0-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>aaf-cadi-cass</artifactId>
+       <name>AAF CADI Cassandra Library</name>
+       <packaging>jar</packaging>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-core</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-aaf</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.apache.cassandra</groupId>
+                       <artifactId>cassandra-all</artifactId>
+                       <version>2.1.14</version>
+                       <scope>compile</scope>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-log4j12</artifactId>
+                               </exclusion>
+                               <exclusion>
+                                       <groupId>log4j</groupId>
+                                       <artifactId>log4j</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+
+       </dependencies>
+       <build>
+               <plugins />
+       </build>
+</project>
diff --git a/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticatedUser.java b/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticatedUser.java
new file mode 100644 (file)
index 0000000..3f08b1e
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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 com.att.aaf.cadi.cass;
+
+import java.security.Principal;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.onap.aaf.cadi.Access;
+
+public class AAFAuthenticatedUser extends AuthenticatedUser implements Principal {
+       private boolean anonymous = false, supr=false, local=false;
+       private String fullName;
+//     private Access access;
+
+       public AAFAuthenticatedUser(Access access, String name) {
+               super(name);
+//             this.access = access;
+           int endIndex = name.indexOf("@");
+           if(endIndex >= 0) {
+               fullName = name;
+           } else {
+               fullName = name + '@' + AAFBase.default_realm;
+           }
+       }
+       
+       public String getFullName() {
+               return fullName;
+       }
+       
+       public String getName() {
+               return fullName;
+       }
+       
+       /* (non-Javadoc)
+        * @see org.apache.cassandra.auth.AuthenticatedUser#isAnonymous()
+        */
+       @Override
+       public boolean isAnonymous() {
+               return anonymous;
+       }
+
+       public void setAnonymous(boolean anon) {
+               anonymous = anon;
+       }
+
+       public boolean getAnonymous() {
+               return anonymous;
+       }
+
+       /* (non-Javadoc)
+        * @see org.apache.cassandra.auth.AuthenticatedUser#isSuper()
+        */
+       @Override
+       public boolean isSuper() {
+               return supr;
+       }
+
+       public void setSuper(boolean supr) {
+               this.supr = supr;
+       }
+
+       public boolean getSuper() {
+               return supr;
+       }
+
+       /**
+        * We check Local so we can compare with the right Lur.  This is AAF Plugin only.
+        * @return
+        */
+       public boolean isLocal() {
+               return local;
+       }
+       
+       public void setLocal(boolean val) {
+               local = val;
+       }
+
+       @Override
+         public boolean equals(Object o) {
+                 if (this == o) return true;
+             if (!(o instanceof AAFAuthenticatedUser)) return false;
+             return ((AuthenticatedUser)o).getName().equals(this.getName());
+         }
+
+         @Override
+         public int hashCode() {
+                 //access.log(Level.DEBUG, "AAFAuthentication hashcode ",getName().hashCode());
+             return getName().hashCode();
+         }  
+}
diff --git a/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticator.java b/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthenticator.java
new file mode 100644 (file)
index 0000000..1f50280
--- /dev/null
@@ -0,0 +1,173 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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 com.att.aaf.cadi.cass;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.auth.IAuthenticator;
+import org.apache.cassandra.auth.ISaslAwareAuthenticator;
+import org.apache.cassandra.exceptions.AuthenticationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CredVal.Type;
+import org.onap.aaf.cadi.config.Config;
+
+public class AAFAuthenticator extends AAFBase implements ISaslAwareAuthenticator  {
+
+       public boolean requireAuthentication() {
+                return true;
+        }
+         
+         /**
+          * Invoked to authenticate an user
+          */
+         public AuthenticatedUser authenticate(Map<String, String> credentials) throws AuthenticationException {
+                   String username = (String)credentials.get("username");
+                   if (username == null) {
+                     throw new AuthenticationException("'username' is missing");
+                   }
+                   
+                   AAFAuthenticatedUser aau = new AAFAuthenticatedUser(access,username);
+                   String fullName=aau.getFullName();
+                   access.log(Level.DEBUG, "Authenticating", aau.getName(),"(", fullName,")");
+                   
+                   String password = (String)credentials.get("password");
+                   if (password == null) {
+                     throw new AuthenticationException("'password' is missing");
+                   } else if(password.startsWith("bsf:")) {
+                       try {
+                                       password = Symm.base64noSplit.depass(password);
+                               } catch (IOException e) {
+                                       throw new AuthenticationException("AAF bnf: Password cannot be decoded");
+                               }
+                       } else if(password.startsWith("enc:")) {
+                               try {
+                                       password = access.decrypt(password, true);
+                               } catch (IOException e) {
+                                       throw new AuthenticationException("AAF Encrypted Password cannot be decrypted");
+                               }
+                   }
+                   
+                   if(localLur!=null) {
+                       access.log(Level.DEBUG, "Validating",fullName, "with LocalTaf", password); 
+                       if(localLur.validate(fullName, Type.PASSWORD, password.getBytes(),null)) {
+                                   aau.setAnonymous(true);
+                                   aau.setLocal(true);
+                                   access.log(Level.DEBUG, fullName, "is authenticated locally"); 
+                               return aau;
+                       }
+                   }
+                   
+                   String aafResponse;
+                   try {
+                       access.log(Level.DEBUG, "Validating",fullName, "with AAF");//, password); 
+                       aafResponse = aafAuthn.validate(fullName, password,null);
+                           if(aafResponse != null) { // Reason for failing.
+                               access.log(Level.AUDIT, "AAF reports ",fullName,":",aafResponse);
+                               throw new AuthenticationException(aafResponse);
+                           }
+                           access.log(Level.AUDIT, fullName, "is authenticated"); //,password);
+                           // This tells Cassandra to skip checking it's own tables for User Entries.
+                           aau.setAnonymous(true);
+                   } catch (AuthenticationException ex) {
+                       throw ex;
+                   } catch(Exception ex) {
+                       access.log(ex,"Exception validating user");                             
+                       throw new AuthenticationException("Exception validating user");
+                   }
+                   
+                   return aau; 
+         }
+         
+         public void create(String username, Map<IAuthenticator.Option, Object> options) throws InvalidRequestException, RequestExecutionException {
+                 access.log(Level.INFO,"Use AAF CLI to create user");
+         }
+         
+         public void alter(String username, Map<IAuthenticator.Option, Object> options) throws RequestExecutionException {
+                 access.log(Level.INFO,"Use AAF CLI to alter user");
+         }
+         
+         public void drop(String username) throws RequestExecutionException {
+                 access.log(Level.INFO,"Use AAF CLI to delete user");
+         }
+         
+         public SaslAuthenticator newAuthenticator() {
+                 return new ISaslAwareAuthenticator.SaslAuthenticator() {
+                   private boolean complete = false;
+                   private Map<String, String> credentials;
+
+                   public byte[] evaluateResponse(byte[] clientResponse) throws AuthenticationException {
+                     this.credentials = decodeCredentials(clientResponse);
+                     this.complete = true;
+                     return null;
+                   }
+
+                   public boolean isComplete() {
+                     return this.complete;
+                   }
+
+                   public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException {
+                     return AAFAuthenticator.this.authenticate(this.credentials);
+                   }
+
+                   private Map<String, String> decodeCredentials(byte[] bytes) throws AuthenticationException {
+                       access.log(Level.DEBUG,"Decoding credentials from client token");
+                     byte[] user = null;
+                     byte[] pass = null;
+                     int end = bytes.length;
+                     for (int i = bytes.length - 1; i >= 0; i--)
+                     {
+                       if (bytes[i] != 0)
+                         continue;
+                       if (pass == null)
+                         pass = Arrays.copyOfRange(bytes, i + 1, end);
+                       else if (user == null)
+                         user = Arrays.copyOfRange(bytes, i + 1, end);
+                       end = i;
+                     }
+
+                     if (user == null)
+                       throw new AuthenticationException("Authentication ID must not be null");
+                     if (pass == null) {
+                       throw new AuthenticationException("Password must not be null");
+                     }
+                     Map<String,String> credentials = new HashMap<String,String>();
+                     try {
+                         credentials.put(IAuthenticator.USERNAME_KEY, new String(user, Config.UTF_8));
+                         credentials.put(IAuthenticator.PASSWORD_KEY, new String(pass, Config.UTF_8));
+                               } catch (UnsupportedEncodingException e) {
+                                       throw new AuthenticationException(e.getMessage());
+                               }
+                     return credentials;
+                   }
+                 };      
+         }
+
+}
+
diff --git a/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthorizer.java b/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFAuthorizer.java
new file mode 100644 (file)
index 0000000..7353ee3
--- /dev/null
@@ -0,0 +1,224 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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 com.att.aaf.cadi.cass;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.auth.IAuthorizer;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.PermissionDetails;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLur;
+import org.onap.aaf.cadi.lur.LocalPermission;
+
+public class AAFAuthorizer extends AAFBase implements IAuthorizer {
+       // Returns every permission on the resource granted to the user.
+    public Set<Permission> authorize(AuthenticatedUser user, IResource resource) {
+       String uname, rname;
+       access.log(Level.DEBUG,"Authorizing",uname=user.getName(),"for",rname=resource.getName());
+
+       Set<Permission> permissions;
+
+       if(user instanceof AAFAuthenticatedUser) {
+               AAFAuthenticatedUser aafUser = (AAFAuthenticatedUser) user;
+                       aafUser.setAnonymous(false);
+                       
+                       if(aafUser.isLocal()) {
+                               permissions = checkPermissions(aafUser, new LocalPermission(
+                                       rname.replaceFirst("data", cluster_name)
+                               ));
+                       } else {
+                               permissions = checkPermissions(
+                                               aafUser,
+                                               perm_type,
+                                               ':'+rname.replaceFirst("data", cluster_name).replace('/', ':'));
+                       }
+       } else {
+               permissions = Permission.NONE;
+       }
+       
+       access.log(Level.INFO,"Permissions on",rname,"for",uname,':', permissions);
+
+        return permissions;
+    }
+    
+    /**
+     * Check only for Localized IDs (see cadi.properties)
+     * @param aau
+     * @param perm
+     * @return
+     */
+    private Set<Permission> checkPermissions(AAFAuthenticatedUser aau, LocalPermission perm) {
+       if(localLur.fish(aau, perm)) {
+//             aau.setSuper(true);
+               return Permission.ALL;
+       } else {
+               return Permission.NONE;
+       }
+    }
+    
+    /**
+     * Check remoted AAF Permissions
+     * @param aau
+     * @param type
+     * @param instance
+     * @return
+     */
+    private Set<Permission> checkPermissions(AAFAuthenticatedUser aau, String type, String instance) {
+               // Can perform ALL actions
+        PermHolder ph = new PermHolder(aau);
+        aafLur.fishOneOf(aau,ph,type,instance,actions);
+        return ph.permissions;
+    }   
+
+    private class PermHolder {
+       private AAFAuthenticatedUser aau;
+               public PermHolder(AAFAuthenticatedUser aau) {
+               this.aau = aau;
+       }
+       public Set<Permission> permissions = Permission.NONE;
+               public void mutable() {
+                       if(permissions==Permission.NONE) {
+                               permissions = new HashSet<Permission>();
+                       }
+               }
+    };
+   /**
+    * This specialty List avoid extra Object Creation, and allows the Lur to do a Vistor on all appropriate Perms
+    */
+   private static final ArrayList<AbsAAFLur.Action<PermHolder>> actions = new ArrayList<AbsAAFLur.Action<PermHolder>>();
+   static {
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "*";
+               }
+               
+               public boolean exec(PermHolder a) {
+               a.aau.setSuper(true);
+               a.permissions = Permission.ALL;
+                       return true;
+               }
+          });
+          
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "SELECT";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.SELECT);
+                       return false;
+               }
+          });
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "MODIFY";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.MODIFY);
+                       return false;
+               }
+          });
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "CREATE";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.CREATE);
+                       return false;
+               }
+          });
+
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "ALTER";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.ALTER);
+                       return false;
+               }
+          });
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "DROP";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.DROP);
+                       return false;
+               }
+          });
+          actions.add(new AbsAAFLur.Action<PermHolder>() {
+               public String getName() {
+                       return "AUTHORIZE";
+               }
+               
+               public boolean exec(PermHolder ph) {
+                       ph.mutable();
+               ph.permissions.add(Permission.AUTHORIZE);
+                       return false;
+               }
+          });
+
+
+   }; 
+   
+   
+    public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to) throws RequestExecutionException {
+       access.log(Level.INFO, "Use AAF CLI to grant permission(s) to user/role");
+    }
+
+    public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from) throws RequestExecutionException {
+       access.log(Level.INFO,"Use AAF CLI to revoke permission(s) for user/role");
+    }
+
+    public Set<PermissionDetails> list(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of) throws RequestValidationException, RequestExecutionException {
+       access.log(Level.INFO,"Use AAF CLI to find the list of permissions");
+       return null;
+    }
+
+    // Called prior to deleting the user with DROP USER query. Internal hook, so no permission checks are needed here.
+    public void revokeAll(String droppedUser) {
+       access.log(Level.INFO,"Use AAF CLI to revoke permission(s) for user/role");
+    }
+
+    // Called after a resource is removed (DROP KEYSPACE, DROP TABLE, etc.).
+    public void revokeAll(IResource droppedResource) {
+       access.log(Level.INFO,"Use AAF CLI to delete the unused permission", droppedResource.getName());
+    }
+
+}
diff --git a/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFBase.java b/cadi/cass/src/main/java/com/att/aaf/cadi/cass/AAFBase.java
new file mode 100644 (file)
index 0000000..b091cc9
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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 com.att.aaf.cadi.cass;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.cassandra.auth.DataResource;
+import org.apache.cassandra.auth.IAuthenticator;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLur;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.lur.LocalLur;
+
+public abstract class AAFBase {
+       protected static final Set<IAuthenticator.Option> options;
+       protected static final Set<DataResource> dataResource;
+
+       static {
+               options = new HashSet<IAuthenticator.Option>();
+               options.add(IAuthenticator.Option.PASSWORD);
+               
+               dataResource = new HashSet<DataResource>();
+               dataResource.add(DataResource.columnFamily("system_auth", "credentials"));
+       }
+       
+       protected static Access access;
+       protected static LocalLur localLur;
+       protected static AAFCon<?> aafcon;
+       protected static AAFAuthn<?> aafAuthn;
+       protected static AbsAAFLur<AAFPermission> aafLur;
+       protected static String default_realm;
+    protected static String cluster_name;
+    protected static String perm_type;
+       private static boolean props_ok = false;
+       
+       /**
+        * If you use your own Access Class, this must be called before 
+        * "setup()" is invoked by Cassandra.
+        * 
+        * Otherwise, it will default to reading Properties CADI style.
+        * 
+        * @param access
+        */
+       public static void setAccess(Access access) {
+               AAFBase.access = access;
+       }
+
+       
+    public void validateConfiguration() throws ConfigurationException {
+       setup();
+       if(!props_ok)  {
+               throw new ConfigurationException("AAF not initialized");
+       }
+    }
+    
+       @SuppressWarnings("unchecked")
+       public synchronized void setup() {
+               if(aafAuthn == null) {
+                       try {
+                               if(access==null) {
+                                       String value = System.getProperty(Config.CADI_PROP_FILES, "cadi.properties");
+                                       Properties initial = new Properties();
+                                       URL cadi_props = ClassLoader.getSystemResource(value);
+                                       if(cadi_props == null) {
+                                               File cp = new File(value);
+                                               if(cp.exists()) {
+                                                       InputStream is = new FileInputStream(cp);
+                                                       try {
+                                                               initial.load(is);
+                                                       } finally {
+                                                               is.close();
+                                                       }
+                                               } else {
+                                                       System.out.printf("%s does not exist as File or in Classpath\n",value);
+                                                       initial.setProperty(Config.CADI_PROP_FILES, value);
+                                               }
+                                       } else {
+                                               InputStream is = cadi_props.openStream();
+                                               try {
+                                                       initial.load(is);
+                                               } finally {
+                                                       is.close();
+                                               }
+                                       }
+                                       access = new PropAccess(initial);
+                               }
+                               props_ok = true;
+                               if((perm_type = Config.logProp(access, "cass_group_name",null))==null) {
+                                       props_ok=false;
+                               } else {
+                                       perm_type = perm_type + ".cass";
+                               }
+                               
+                               if((cluster_name = Config.logProp(access,"cass_cluster_name",null))==null) {
+                                       if((cluster_name = DatabaseDescriptor.getClusterName())==null) {
+                                               props_ok=false;
+                                       }
+                               }
+
+                               if((default_realm = Config.logProp(access, Config.AAF_DEFAULT_REALM, null))==null) {
+                                       props_ok=false;
+                               }
+                               
+                               if(props_ok==false) {
+                                       return;
+                               }
+
+                               // AAFLur has pool of DME clients as needed, and Caches Client lookups
+                               SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class);
+                               Lur lur = Config.configLur(si,aafcon);
+                               // Loop through to find AAFLur out of possible Lurs, to reuse AAFCon
+                               if(lur instanceof EpiLur) {
+                                       EpiLur elur = (EpiLur)lur;
+                                       for(int i=0; (lur = elur.get(i))!=null;++i) {
+                                               if(lur instanceof AbsAAFLur) {
+                                                       aafLur=(AbsAAFLur<AAFPermission>)lur;
+                                                       aafcon = aafLur.aaf;
+                                                       aafAuthn = aafLur.aaf.newAuthn(aafLur);
+                                                       break;
+                                               } else if(lur instanceof LocalLur) {
+                                                       localLur = (LocalLur)lur;
+                                               }
+                                       }
+                               } else if(lur instanceof AbsAAFLur) {
+                                       aafLur=(AbsAAFLur<AAFPermission>)lur;
+                                       aafcon = aafLur.aaf;
+                                       aafAuthn = aafLur.aaf.newAuthn(aafLur);
+                               }
+                               if(aafAuthn==null) {
+                                       access.log(Level.INIT,"Failed to instantiate full AAF access");
+                                       props_ok = false;
+                               }
+                       } catch (Exception e) {
+                               aafAuthn=null;
+                               if(access!=null)access.log(e, "Failed to initialize AAF");
+                               props_ok = false;
+                       }
+               }               
+       }
+
+       public Set<DataResource> protectedResources() {
+               access.log(Level.DEBUG, "Data Resource asked for: it's",dataResource.isEmpty()?"":"not","empty");
+               return dataResource;
+       }
+       
+       public Set<IAuthenticator.Option> supportedOptions() {
+               access.log(Level.DEBUG, "supportedOptions() called");
+               return options;
+       }
+         
+       public Set<IAuthenticator.Option> alterableOptions() {
+               access.log(Level.DEBUG, "alterableOptions() called");
+               return options;
+       }
+
+
+}
diff --git a/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticatedUserTest.java b/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticatedUserTest.java
new file mode 100644 (file)
index 0000000..2f19672
--- /dev/null
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.cass;
+
+import static org.junit.Assert.*;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.att.aaf.cadi.cass.AAFAuthenticatedUser;
+
+public class JU_AAFAuthenticatedUserTest {
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               AAFAuthenticatedUser user = new AAFAuthenticatedUser(null, "User1@aaf");
+               assertEquals(user.getFullName(),"User1@aaf");
+               assertEquals(user.getName(),"User1@aaf");
+               assertFalse(user.isAnonymous());
+               assertFalse(user.isSuper());
+               assertFalse(user.isLocal());
+               
+               
+               
+       }
+       
+       @Test
+       public void testone() {
+               AAFAuthenticatedUser user = new AAFAuthenticatedUser(null, "User2@aaf");
+               assertEquals(user.getFullName(),"User2@aaf");
+               assertEquals(user.getName(),"User2@aaf");
+               assertFalse(user.isAnonymous());
+               assertFalse(user.isSuper());
+               assertFalse(user.isLocal());
+               
+               
+               
+       }
+
+       @Test
+       public void testtwo() {
+               AAFAuthenticatedUser user = new AAFAuthenticatedUser(null, "onap@aaf");
+               assertEquals(user.getFullName(),"onap@aaf");
+               assertEquals(user.getName(),"onap@aaf");
+               assertFalse(user.isAnonymous());
+               assertFalse(user.isSuper());
+               assertFalse(user.isLocal());
+               
+               
+               
+       }
+       
+       @Test
+       public void testthree() {
+               AAFAuthenticatedUser user = new AAFAuthenticatedUser(null, "openecomp@aaf");
+               assertEquals(user.getFullName(),"openecomp@aaf");
+               assertEquals(user.getName(),"openecomp@aaf");
+               assertFalse(user.isAnonymous());
+               assertFalse(user.isSuper());
+               assertFalse(user.isLocal());
+               
+               
+               
+       }
+
+}
diff --git a/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticator.java b/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthenticator.java
new file mode 100644 (file)
index 0000000..2dcb033
--- /dev/null
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.cass;
+
+import static org.junit.Assert.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.exceptions.AuthenticationException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import com.att.aaf.cadi.cass.AAFAuthenticatedUser;
+import com.att.aaf.cadi.cass.AAFAuthenticator;
+import com.att.aaf.cadi.cass.AAFBase;
+
+import junit.framework.Assert;
+
+public class JU_AAFAuthenticator extends AAFBase
+{
+       
+
+       @Before
+       public void setUp()
+       {
+               
+       }
+
+       @After
+       public void tearDown()
+       {
+               
+       }
+       
+       @Test
+       public void checkRequiredAuth() {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Assert.assertTrue(test.requireAuthentication());
+       }
+       //TODO: Call may be broken due to missing ATT specific code
+       @Test
+       public void checkAuthenticate()  throws AuthenticationException {
+               AuthenticatedUser user = new AuthenticatedUser("testUser");
+               AAFAuthenticator test = new AAFAuthenticator(); 
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", "testUser");
+               cred.put("password", "testPass");
+               String username = (String)cred.get("username");
+               Access access = new PropAccess();
+               AAFAuthenticatedUser aau = new AAFAuthenticatedUser(access,username);
+               String fullName=aau.getFullName();
+               access.log(Level.DEBUG, "Authenticating", aau.getName(),"(", fullName,")");
+               //test.authenticate(cred);
+               //Assert.assert
+               
+       }
+       
+       @Test(expected = AuthenticationException.class)
+       public void checkThrowsUser() throws AuthenticationException {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", null);
+               Assert.assertNull(cred.get("username"));
+               test.authenticate(cred);
+       }
+       
+       @Test(expected = AuthenticationException.class)
+       public void checkThrowsPass() throws AuthenticationException {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", "testUser");
+               cred.put("password", "bsf:");
+               Assert.assertNotNull(cred.get("password"));
+               test.authenticate(cred);
+               
+               cred.put("password", null);
+               Assert.assertNull(cred.get("password"));
+               test.authenticate(cred);
+       }
+
+
+
+}
diff --git a/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthorizerTest.java b/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFAuthorizerTest.java
new file mode 100644 (file)
index 0000000..6f2e9da
--- /dev/null
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.cass;
+
+import static org.junit.Assert.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.exceptions.AuthenticationException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import com.att.aaf.cadi.cass.AAFAuthenticatedUser;
+import com.att.aaf.cadi.cass.AAFAuthenticator;
+import com.att.aaf.cadi.cass.AAFBase;
+
+import junit.framework.Assert;
+//TODO:DELETE THIS OLD TEST
+public class JU_AAFAuthorizerTest extends AAFBase
+{
+       
+
+       @Before
+       public void setUp()
+       {
+               
+       }
+
+       @After
+       public void tearDown()
+       {
+               
+       }
+       
+       @Test
+       public void checkRequiredAuth() {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Assert.assertTrue(test.requireAuthentication());
+       }
+       
+       @Test
+       public void checkAuthenticate()  throws AuthenticationException {
+               AuthenticatedUser user = new AuthenticatedUser("testUser");
+               AAFAuthenticator test = new AAFAuthenticator(); 
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", "testUser");
+               cred.put("password", "testPass");
+               String username = (String)cred.get("username");
+               AAFAuthenticatedUser aau = new AAFAuthenticatedUser(access,username);
+               String fullName=aau.getFullName();
+               //access.log(Level.DEBUG, "Authenticating", aau.getName(),"(", fullName,")");
+               test.authenticate(cred);
+               //Assert.assert
+               
+       }
+       
+       @Test(expected = AuthenticationException.class)
+       public void checkThrowsUser() throws AuthenticationException {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", null);
+               Assert.assertNull(cred.get("username"));
+               test.authenticate(cred);
+       }
+       
+       @Test(expected = AuthenticationException.class)
+       public void checkThrowsPass() throws AuthenticationException {
+               AAFAuthenticator test = new AAFAuthenticator();
+               Map<String, String> cred = new HashMap<String,String>();
+               cred.put("username", "testUser");
+               cred.put("password", "bsf:");
+               Assert.assertNotNull(cred.get("password"));
+               test.authenticate(cred);
+               
+               cred.put("password", null);
+               Assert.assertNull(cred.get("password"));
+               test.authenticate(cred);
+       }
+
+
+
+}
diff --git a/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFBaseTest.java b/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_AAFBaseTest.java
new file mode 100644 (file)
index 0000000..4985486
--- /dev/null
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.cass;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+
+import junit.framework.Assert;
+
+import com.att.aaf.cadi.cass.AAFBase;
+
+import static org.junit.Assert.*;
+
+import org.apache.cassandra.exceptions.ConfigurationException;
+
+public class JU_AAFBaseTest
+{
+       
+       //TODO: REmove this file, no need for junit for abstract class
+       @Before
+       public void setUp()
+       {
+               
+       }
+
+       @After
+       public void tearDown()
+       {
+               
+       }
+
+       
+       @Test
+       public void test_method_setAccess_0_branch_0()
+       {
+               System.out.println("Now Testing Method:setAccess Branch:0");
+               
+               //Call Method
+               AAFBase.setAccess(null);
+               
+       }
+       
+       
+       
+
+}
diff --git a/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_CASS.java b/cadi/cass/src/test/java/org/onap/aaf/cadi/cass/JU_CASS.java
new file mode 100644 (file)
index 0000000..60fd548
--- /dev/null
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.cass;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.auth.Permission;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.aaf.cadi.cass.AAFAuthenticator;
+import com.att.aaf.cadi.cass.AAFAuthorizer;
+
+public class JU_CASS {
+
+       private static AAFAuthenticator aa;
+       private static AAFAuthorizer an;
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+               System.setProperty("cadi_prop_files", "etc/cadi.properties");
+               
+               aa = new AAFAuthenticator();
+               an = new AAFAuthorizer();
+
+               aa.setup();
+               an.setup(); // does nothing after aa.
+               
+               aa.validateConfiguration();
+               
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() throws Exception {
+                       Map<String,String> creds = new HashMap<String,String>();
+                       creds.put("username", "XXX@NS");
+                       creds.put("password", "enc:???");
+                       AuthenticatedUser aaf = aa.authenticate(creds);
+
+                       // Test out "aaf_default_domain
+                       creds.put("username", "XX");
+                       aaf = aa.authenticate(creds);
+                       
+                       IResource resource = new IResource() {
+                               public String getName() {
+                                       return "data/authz";
+                               }
+
+                               public IResource getParent() {
+                                       return null;
+                               }
+
+                               public boolean hasParent() {
+                                       return false;
+                               }
+
+                               public boolean exists() {
+                                       return true;
+                               }
+                               
+                       };
+                       
+                       Set<Permission> perms = an.authorize(aaf, resource);
+                       
+                       // Test out "AAF" access
+                       creds.put("username", "XXX@NS");
+                       creds.put("password", "enc:???");
+                       aaf = aa.authenticate(creds);
+                       perms = an.authorize(aaf, resource);
+                       Assert.assertFalse(perms.isEmpty());
+
+                       perms = an.authorize(aaf, resource);
+                       Assert.assertFalse(perms.isEmpty());
+                       
+       }
+
+}
diff --git a/cadi/client/.gitignore b/cadi/client/.gitignore
new file mode 100644 (file)
index 0000000..8f9d962
--- /dev/null
@@ -0,0 +1,5 @@
+/.project
+/.settings/
+/target/
+/logs/
+/.classpath
diff --git a/cadi/client/pom.xml b/cadi/client/pom.xml
new file mode 100644 (file)
index 0000000..1081ab0
--- /dev/null
@@ -0,0 +1,89 @@
+<!--
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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====================================================
+ *
+-->
+<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.aaf.cadi</groupId>
+       <artifactId>parent</artifactId>
+       <version>1.5.0-SNAPSHOT</version>
+       <relativePath>..</relativePath>
+</parent>
+
+       <artifactId>aaf-cadi-client</artifactId>
+       <name>AAF CADI Client</name>
+       <packaging>jar</packaging>
+       <modelVersion>4.0.0</modelVersion>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onap.aaf.misc</groupId>
+                       <artifactId>aaf-misc-rosetta</artifactId>
+                       
+               </dependency>
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-core</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+                       <scope>compile</scope>
+               </dependency>
+       </dependencies>
+
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jarsigner-plugin</artifactId>
+                       </plugin>
+               </plugins>
+       </build>
+       
+</project>
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java
new file mode 100644 (file)
index 0000000..7a32473
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+/**
+ * AbsAuthentication is a class representing how to Authenticate onto a Client.
+ * 
+ * Methods of setting Authentication on a Client vary, so CLIENT is a Generic Type
+ * This allows the ability to apply security onto Different Client Types, as they come 
+ * into vogue, or change over time.
+ * 
+ * Password is encrypted at rest.
+ *  
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class AbsAuthentication<CLIENT> implements SecuritySetter<CLIENT> {
+       // HTTP Header for Authentication is "Authorization".  This was from an early stage of internet where 
+       // Access by Credential "Authorized" you for everything on the site.  Since those early days, it became
+       // clear that "full access" wasn't appropriate, so the split between Authentication and Authorization
+       // came into being... But the Header remains.
+       public static final String AUTHORIZATION = "Authorization";
+       private static final Symm symm;
+
+       protected static final String REPEAT_OFFENDER = "This call is aborted because of repeated usage of invalid Passwords";
+       private static final int MAX_TEMP_COUNT = 10;
+       private static final int MAX_SPAM_COUNT = 10000;
+       private static final long WAIT_TIME = 1000*60*4;
+       private final byte[] headValue;
+       private String user;
+       protected final SecurityInfoC<CLIENT> securityInfo;
+       protected long lastMiss;
+       protected int count;
+       
+       static {
+               try {
+                       symm = Symm.encrypt.obtain();
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot create critical internal encryption key",e);
+               }
+               
+       }
+
+       public AbsAuthentication(final SecurityInfoC<CLIENT> securityInfo, final String user, final byte[] headValue) throws IOException {
+               this.headValue = headValue==null?null:symm.encode(headValue);
+               this.user = user;
+               this.securityInfo = securityInfo;
+               lastMiss=0L;
+               count=0;
+       }
+
+       protected String headValue() throws IOException {
+               if(headValue==null) {
+                       return "";
+               } else {
+                       return new String(symm.decode(headValue));
+               }
+       }
+       
+       protected void setUser(String id) {
+               user = id;
+       }
+       
+       @Override
+       public String getID() {
+               return user;
+       }
+
+       public boolean isDenied() {
+               if(lastMiss>0 && lastMiss>System.currentTimeMillis()) {
+                       return true;
+               } else {
+                       lastMiss=0L;
+                       return false;
+               }
+       }
+
+       public synchronized int setLastResponse(int httpcode) {
+               if(httpcode == 401) {
+                       ++count;
+                       if(lastMiss==0L && count>MAX_TEMP_COUNT) {
+                               lastMiss=System.currentTimeMillis()+WAIT_TIME;
+                       }
+                       //                              if(count>MAX_SPAM_COUNT) {
+                       //                                      System.err.printf("Your service has %d consecutive bad service logins to AAF. \nIt will now exit\n",
+                       //                                                      count);
+                       //                                      System.exit(401);
+                       //                              }
+                       if(count%1000==0) {
+                               System.err.printf("Your service has %d consecutive bad service logins to AAF. AAF Access will be disabled after %d\n",
+                                               count,MAX_SPAM_COUNT);
+                       }
+
+               } else {
+                       lastMiss=0;
+               }
+               return count;
+       }
+
+       public int count() {
+               return count;
+       }
+
+}
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java
new file mode 100644 (file)
index 0000000..3815bc6
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import java.security.Principal;
+
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+/**
+ * This client represents the ability to Transfer the Identity of the caller to the authenticated
+ * user being transferred to.  This ability is critical for App-to-App communication to ensure that 
+ * Authorization can happen on the End-Users' credentials when appropriate, even though Authentication
+ * to App1 by App2 must be by App2's credentials.
+ *  
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class AbsTransferSS<CLIENT> implements SecuritySetter<CLIENT> {
+       protected String value;
+       protected SecurityInfoC<CLIENT> securityInfo;
+       protected SecuritySetter<CLIENT> defSS;
+       private Principal principal;
+
+       //Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]*
+       public AbsTransferSS(TaggedPrincipal principal, String app) {
+               init(principal, app);
+       }
+
+       public AbsTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<CLIENT> si) {
+               init(principal,app);
+               securityInfo = si;
+               this.defSS = si.defSS;
+       }
+
+       private void init(TaggedPrincipal principal, String app)  {
+               this.principal=principal;
+               if(principal==null) {
+                       return;
+               } else  {
+                       value = principal.getName() + ':' + 
+                                       app + ':' + 
+                                       principal.tag() + ':' +
+                                       "AS";
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.SecuritySetter#getID()
+        */
+       @Override
+       public String getID() {
+               return principal==null?"":principal.getName();
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java
new file mode 100644 (file)
index 0000000..1eb8d7c
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+/**
+ * Basic Auth is a marker Interface, because certain kinds of behaviors apply only to User/Password Combinations
+ */
+public interface BasicAuth {
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java
new file mode 100644 (file)
index 0000000..d5dfebf
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+
+public interface EClient<CT> {
+       public void setMethod(String meth);
+       public void setPathInfo(String pathinfo);
+       public void setPayload(Transfer transfer);
+       public void addHeader(String tag, String value);
+       public void setQueryParams(String q);
+       public void setFragment(String f);
+       public void send() throws APIException;
+       public<T> Future<T> futureCreate(Class<T> t);
+       public Future<String> futureReadString();
+       public<T> Future<T> futureRead(RosettaDF<T> df,Data.TYPE type);
+       public<T> Future<T> future(T t);
+       public Future<Void> future(HttpServletResponse resp, int expected) throws APIException;
+       
+       public interface Transfer {
+               public void transfer(OutputStream os) throws IOException, APIException;
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java
new file mode 100644 (file)
index 0000000..2579dc1
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import org.onap.aaf.cadi.CadiException;
+
+public abstract class Future<T> {
+       public T value;
+       public abstract boolean get(int timeout) throws CadiException;
+       
+       public abstract int code();
+       public abstract String body();
+       public abstract String header(String tag);
+}
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java
new file mode 100644 (file)
index 0000000..c13afc2
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+/**
+ * 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/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java
new file mode 100644 (file)
index 0000000..5ebc017
--- /dev/null
@@ -0,0 +1,974 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.util.Enumeration;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.EClient.Transfer;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Pool;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+public abstract class Rcli<CT> {
+       public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
+       public static final String APPL_JSON = "application/json";
+       public static final String APPL_XML = "application/xml";
+       public static final String BLANK = "";
+       public static final String CONTENT_TYPE = "Content-Type";
+       public static final String ACCEPT = "Accept";
+
+       protected static final String POST = "POST";
+       protected static final String GET = "GET";
+       protected static final String PUT = "PUT";
+       protected static final String DELETE = "DELETE";
+       protected TYPE type;
+       protected String apiVersion;
+       protected int readTimeout = 5000;
+       protected int connectionTimeout = 3000;
+       protected URI uri;
+       private String queryParams, fragment;
+       public static Pool<byte[]> buffPool = new Pool<byte[]>(new Pool.Creator<byte[]>() {
+               @Override
+               public byte[] create() throws APIException {
+                       return new byte[1024];
+               }
+
+               @Override
+               public void destroy(byte[] t) {
+               }
+
+               @Override
+               public boolean isValid(byte[] t) {
+                       return true;
+               }
+
+               @Override
+               public void reuse(byte[] t) {
+               }
+       });
+
+
+       public Rcli() {
+               super();
+       }
+
+       public abstract void setSecuritySetter(SecuritySetter<CT> ss);
+       public abstract SecuritySetter<CT> getSecuritySetter();
+
+
+       public Rcli<CT> forUser(SecuritySetter<CT> ss) {
+               Rcli<CT> rv = clone(uri==null?this.uri:uri,ss);
+               setSecuritySetter(ss);
+               rv.type = type;
+               rv.apiVersion = apiVersion;
+               return rv;
+       }
+       
+       protected abstract Rcli<CT> clone(URI uri, SecuritySetter<CT> ss);
+       
+       public abstract void invalidate() throws CadiException;
+
+       public Rcli<CT> readTimeout(int millis) {
+               readTimeout = millis;
+               return this;
+       }
+
+       public Rcli<CT> connectionTimeout(int millis) {
+               connectionTimeout = millis;
+               return this;
+       }
+
+       public Rcli<CT> type(TYPE type) {
+               this.type=type;
+               return this;
+       }
+
+       public Rcli<CT> apiVersion(String apiVersion) {
+               this.apiVersion = apiVersion;
+               return this;
+       }
+       
+       public boolean isApiVersion(String prospective) {
+               return apiVersion.equals(prospective);
+       }
+
+
+       public String typeString(Class<?> cls) {
+               return "application/"+cls.getSimpleName()+"+"+type.name().toLowerCase()+
+                               (apiVersion==null?BLANK:";version="+apiVersion);
+       }
+
+       protected abstract EClient<CT> client() throws CadiException;
+
+
+       public<T> Future<T> create(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,contentType);
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureCreate(df.getTypeClass());
+       }
+
+       public<T> Future<T> create(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureCreate(df.getTypeClass());
+       }
+
+       public<T> Future<T> create(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,typeString(cls));
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureCreate(df.getTypeClass());
+       }
+
+       public<T> Future<T> create(String pathinfo, Class<T> cls) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,typeString(cls));
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureCreate(cls);
+       }
+
+       public Future<Void> create(String pathinfo, String contentType) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,contentType);
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureCreate(Void.class);
+       }
+
+
+       /**
+        * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc
+        * Note Shortcut:
+        *   Because typically, you will want to have a variable as value, you can type, as long as tag ends with "="
+        *   postForm(..., "tag1=value1","tag2=",var2);
+        * @param pathinfo
+        * @param df
+        * @param cls
+        * @param formParam
+        * @return
+        * @throws APIException
+        * @throws CadiException
+        */
+       public <T> Future<T> postForm(String pathinfo, final RosettaDF<T> df, final String ... formParam) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,FORM_ENCODED);
+               switch(type) {
+                       case JSON:
+                               client.addHeader(ACCEPT, APPL_JSON);
+                               break;
+                       case XML:
+                               client.addHeader(ACCEPT, APPL_XML);
+                               break;
+                       default:
+                               break;
+               }
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               PrintStream ps;
+                               if(os instanceof PrintStream) {
+                                       ps = (PrintStream)os;
+                               } else {
+                                       ps = new PrintStream(os);
+                               }
+                               boolean first = true;
+                               for(String fp : formParam) {
+                                       if(fp!=null) {
+                                               if(first) {
+                                                       first = false;
+                                               } else {
+                                                       ps.print('&');
+                                               }
+                                               if(fp.endsWith("=")) {
+                                                       first = true;
+                                               }
+                                               ps.print(fp);
+                                       }
+                               }
+                       }});
+               client.send();
+               queryParams = fragment = null;
+               return client.futureRead(df,TYPE.JSON);
+       }
+
+       /**
+        * Read String, using POST for keyInfo
+        * 
+        * @param pathinfo
+        * @param df
+        * @param t
+        * @param resp
+        * @return
+        * @throws APIException
+        * @throws CadiException
+        */
+       public<T> Future<String> readPost(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureReadString();
+       }
+
+       /**
+        * Read using POST for keyInfo, responding with marshaled Objects
+        *
+        * @param pathinfo
+        * @param df
+        * @param t
+        * @param resp
+        * @return
+        * @throws APIException
+        * @throws CadiException
+        */
+       public<T,R> Future<R> readPost(String pathinfo, final RosettaDF<T> df, final T t, final RosettaDF<R> resp) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass()));
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureRead(resp,resp.getOutType());
+       }
+
+       public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(POST);
+               client.addHeader(CONTENT_TYPE,contentType);
+               client.setPathInfo(pathinfo);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                       }});
+               client.send();
+               queryParams = fragment = null;
+               return client.futureReadString();
+       }
+
+       public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException {
+               final String qp;
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+       
+               EClient<CT> client = client();
+               client.setMethod(GET);
+               client.addHeader(ACCEPT, accept);
+               
+               for(int i=1;i<headers.length;i=i+2) {
+                       client.addHeader(headers[i-1],headers[i]);
+               }
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+       
+               client.setPathInfo(pathinfo);
+               
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureReadString();
+       }
+
+       public<T> Future<T> read(String pathinfo, String accept, RosettaDF<T> df, String ... headers) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(GET);
+               client.addHeader(ACCEPT, accept);
+               for(int i=1;i<headers.length;i=i+2) {
+                       client.addHeader(headers[i-1],headers[i]);
+               }
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureRead(df,type);
+       }
+
+       public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(GET);
+               client.addHeader(ACCEPT, typeString(df.getTypeClass()));
+               for(int i=1;i<headers.length;i=i+2) {
+                       client.addHeader(headers[i-1],headers[i]);
+               }
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureRead(df,type);
+       }
+
+       public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(GET);
+               client.addHeader(ACCEPT, typeString(cls));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.futureRead(df,type);
+       }
+
+       public<T> Future<T> update(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(PUT);
+               client.addHeader(CONTENT_TYPE,contentType);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+       
+       public<T> Future<String> updateRespondString(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(PUT);
+               client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.futureReadString();
+       }
+
+
+       public<T> Future<T> update(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(PUT);
+               client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+       
+       public<T> Future<T> update(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(PUT);
+               client.addHeader(CONTENT_TYPE, typeString(cls));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+
+       /**
+        * A method to update with a VOID
+        * @param pathinfo
+        * @param resp
+        * @param expected
+        * @return
+        * @throws APIException
+        * @throws CadiException
+        */
+       public<T> Future<Void> update(String pathinfo) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(PUT);
+               client.addHeader(CONTENT_TYPE, typeString(Void.class));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+//             client.setPayload(new EClient.Transfer() {
+//                     @Override
+//                     public void transfer(OutputStream os) throws IOException, APIException {
+//                     }
+//             });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(null);
+       }
+
+       public<T> Future<T> delete(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(DELETE);
+               client.addHeader(CONTENT_TYPE, contentType);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+
+       public<T> Future<T> delete(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(DELETE);
+               client.addHeader(CONTENT_TYPE, typeString(cls));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+
+       public<T> Future<T> delete(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(DELETE);
+               client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass()));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(new EClient.Transfer() {
+                       @Override
+                       public void transfer(OutputStream os) throws IOException, APIException {
+                               df.newData().out(type).direct(t,os);
+                       }
+               });
+
+               client.send();
+               queryParams = fragment = null;
+               return client.future(t);
+       }
+
+
+       public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+
+               EClient<CT> client = client();
+               client.setMethod(DELETE);
+               client.addHeader(CONTENT_TYPE, typeString(cls));
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.future((T)null);
+       }
+
+       public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException {
+               final String qp; 
+               if(pathinfo==null) {
+                       qp=queryParams;
+               } else {
+                       final int idx = pathinfo.indexOf('?');
+                       if(idx>=0) {
+                               qp=pathinfo.substring(idx+1);
+                               pathinfo=pathinfo.substring(0,idx);
+                       } else {
+                               qp=queryParams;
+                       }
+               }
+
+               EClient<CT> client = client();
+               client.setMethod(DELETE);
+               client.addHeader(CONTENT_TYPE, contentType);
+               client.setQueryParams(qp);
+               client.setFragment(fragment);
+               client.setPathInfo(pathinfo);
+               client.setPayload(null);
+               client.send();
+               queryParams = fragment = null;
+               return client.future(null);
+       }
+
+       public Future<Void> transfer(final HttpServletRequest req, final HttpServletResponse resp, final String pathParam, final int expected) throws CadiException, APIException {
+               EClient<CT> client = client();
+               URI uri;
+               try {
+                       uri = new URI(req.getRequestURI());
+               } catch (Exception e) {
+                       throw new CadiException("Invalid incoming URI",e);
+               }
+               String name;
+               for(Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) {
+                       name = en.nextElement();
+                       client.addHeader(name,req.getHeader(name));
+               }
+               client.setQueryParams(req.getQueryString());
+               client.setFragment(uri.getFragment());
+               client.setPathInfo(pathParam);
+               String meth = req.getMethod();
+               client.setMethod(meth);
+               if(!"GET".equals(meth)) {
+                       client.setPayload(new EClient.Transfer() {
+                               @Override
+                               public void transfer(OutputStream os) throws IOException, APIException {
+                                       final ServletInputStream is = req.getInputStream();
+                                       int read;
+                                       // reuse Buffers
+                                       Pooled<byte[]> pbuff = buffPool.get();
+                                       try { 
+                                               while((read=is.read(pbuff.content))>=0) {
+                                                       os.write(pbuff.content,0,read);
+                                               }
+                                       } finally {
+                                               pbuff.done();
+                                       }
+                               }
+                       });
+               }
+               client.send();
+               return client.future(resp, expected);
+       }
+
+       public String toString() {
+               return uri.toString();
+       }
+
+       /**
+        * @param queryParams the queryParams to set
+        * @return 
+        */
+       public Rcli<CT> setQueryParams(String queryParams) {
+               this.queryParams = queryParams;
+               return this;
+       }
+       
+
+       /**
+        * @param fragment the fragment to set
+        * @return 
+        */
+       public Rcli<CT> setFragment(String fragment) {
+               this.fragment = fragment;
+               return this;
+       }
+
+       public URI getURI() {
+               return uri;
+       }
+
+}
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java
new file mode 100644 (file)
index 0000000..fecb847
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+public class Result<T> {
+       public final int code;
+       public final T value;
+       public final String error;
+
+       private Result(int code, T value, String error) {
+               this.code = code;
+               this.value = value;
+               this.error = error;
+       }
+
+       public static<T> Result<T> ok(int code,T t) {
+               return new Result<T>(code,t,null);
+       }
+       
+       public static<T> Result<T> err(int code,String body) {
+               return new Result<T>(code,null,body);
+       }
+
+       public static<T> Result<T> err(Result<?> r) {
+               return new Result<T>(r.code,null,r.error);
+       }
+
+       public boolean isOK() {
+               return error==null;
+       }
+       
+       public String toString() {
+               StringBuilder sb = new StringBuilder("Code: ");
+               sb.append(code);
+               if(error!=null) {
+                       sb.append(" = ");
+                       sb.append(error);
+               }
+               return sb.toString();
+       }
+}
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java
new file mode 100644 (file)
index 0000000..8208efe
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client;
+
+import java.net.ConnectException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.misc.env.APIException;
+
+/**
+ * 
+ * @author Jonathan
+ *
+ * @param <RT>
+ * @param <RET>
+ */
+public abstract class Retryable<RET> {
+       // be able to hold state for consistent Connections.  Not required for all connection types.
+       public Rcli<?> lastClient;
+       private Locator.Item item;
+       
+       public Retryable() {
+               lastClient = null;
+               item = null;
+       }
+
+       public Retryable(Retryable<?> ret) {
+               lastClient = ret.lastClient;
+               item = ret.item;
+       }
+
+       public Locator.Item item(Locator.Item item) {
+               lastClient = null;
+               this.item = item;
+               return item;
+       }
+       public Locator.Item item() {
+               return item;
+       }
+       
+       public abstract RET code(Rcli<?> client) throws CadiException, ConnectException, APIException;
+
+       /**
+        * Note, Retryable is tightly coupled to the Client Utilizing.  It will not be the wrong type.
+        * @return
+        */
+       @SuppressWarnings("unchecked")
+       public <CLIENT> Rcli<CLIENT> lastClient() {
+               return (Rcli<CLIENT>)lastClient;
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java
new file mode 100644 (file)
index 0000000..787c5c2
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HAuthorizationHeader extends AbsAuthentication<HttpURLConnection> {
+       
+       public HAuthorizationHeader(SecurityInfoC<HttpURLConnection> si, String user, String headValue) throws IOException {
+               super(si,user,headValue==null?null:headValue.getBytes());
+       }
+
+       @Override
+       public void setSecurity(HttpURLConnection huc) throws CadiException {
+               if(isDenied()) {
+                       throw new CadiException(REPEAT_OFFENDER);
+               }
+               try {
+                       huc.addRequestProperty(AUTHORIZATION , headValue());
+               } catch (IOException e) {
+                       throw new CadiException(e);
+               }
+               if(securityInfo!=null && huc instanceof HttpsURLConnection) {
+                       securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+               }
+       }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java
new file mode 100644 (file)
index 0000000..9e86c7f
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.client.BasicAuth;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+
+public class HBasicAuthSS extends HAuthorizationHeader implements BasicAuth {
+       public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String password) throws IOException {
+               super(si, user, "Basic " + Symm.base64noSplit.encode(user + ':' + password));
+       }
+
+       public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException {
+               this(si,si.access.getProperty(Config.AAF_APPID, null),
+                               si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false));
+       }
+       
+       public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, boolean setDefault) throws IOException {
+               this(si,si.access.getProperty(Config.AAF_APPID, null),
+                               si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false),setDefault);
+       }
+       
+
+       public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String pass, boolean asDefault) throws IOException {
+               this(si, user,pass);
+               if(asDefault) {
+                       si.set(this);
+               }
+       }
+       
+       public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si) throws IOException {
+               this(si, bp.getName(),new String(bp.getCred()));
+       }
+       
+       public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws IOException {
+               this(si, bp.getName(),new String(bp.getCred()));
+               if(asDefault) {
+                       si.set(this);
+               }
+       }
+
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java
new file mode 100644 (file)
index 0000000..4609988
--- /dev/null
@@ -0,0 +1,444 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.client.EClient;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.env.Data.TYPE;
+import org.onap.aaf.misc.env.util.Pool.Pooled;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+
+/**
+ * Low Level Http Client Mechanism. Chances are, you want the high level "HRcli"
+ * for Rosetta Object Translation
+ * 
+ * @author Jonathan
+ *
+ */
+public class HClient implements EClient<HttpURLConnection> {
+       private URI uri;
+       private ArrayList<Header> headers;
+       private String meth;
+       private String pathinfo;
+       private String query;
+       private String fragment;
+       private Transfer transfer;
+       private SecuritySetter<HttpURLConnection> ss;
+       private HttpURLConnection huc;
+       private int connectTimeout;
+
+       public HClient(SecuritySetter<HttpURLConnection> ss, URI uri,int connectTimeout) throws LocatorException {
+               if (uri == null) {
+                       throw new LocatorException("No Service available to call");
+               }
+               this.uri = uri;
+               this.ss = ss;
+               this.connectTimeout = connectTimeout;
+               pathinfo = query = fragment = null; 
+       }
+
+       @Override
+       public void setMethod(String meth) {
+               this.meth = meth;
+       }
+
+       @Override
+       public void setPathInfo(String pathinfo) {
+               this.pathinfo = pathinfo;
+       }
+
+       @Override
+       public void setPayload(Transfer transfer) {
+               this.transfer = transfer;
+       }
+       
+       @Override
+       public void addHeader(String tag, String value) {
+               if (headers == null)
+                       headers = new ArrayList<Header>();
+               headers.add(new Header(tag, value));
+       }
+
+       @Override
+       public void setQueryParams(String q) {
+               query = q;
+       }
+
+       @Override
+       public void setFragment(String f) {
+               fragment = f;
+       }
+
+       @Override
+       public void send() throws APIException {
+               try {
+                       // Build URL from given URI plus current Settings
+                       if(uri.getPath()==null) {
+                               throw new APIException("Invalid URL entered for HClient");
+                       }
+                       StringBuilder pi=null;
+                       if(pathinfo!=null) { // additional pathinfo
+                               pi = new StringBuilder(uri.getPath());
+                               if(!pathinfo.startsWith("/")) {
+                                       pi.append('/');
+                               }
+                               pi.append(pathinfo);
+                       }
+                       URL url = new URI(
+                                       uri.getScheme(), 
+                                       uri.getUserInfo(),
+                                       uri.getHost(), 
+                                       uri.getPort(), 
+                                       pi==null?uri.getPath():pi.toString(), 
+                                       query,
+                                       fragment).toURL();
+                       pathinfo=null;
+                       query=null;
+                       fragment=null;
+                       huc = (HttpURLConnection) url.openConnection();
+                       huc.setRequestMethod(meth);
+                       if(ss!=null) {
+                               ss.setSecurity(huc); 
+                       }
+                       if (headers != null)
+                               for (Header d : headers) {                                      
+                                       huc.addRequestProperty(d.tag, d.value);
+                               }
+                       huc.setDoInput(true);
+                       huc.setDoOutput(true);
+                       huc.setUseCaches(false);
+                       huc.setConnectTimeout(connectTimeout);
+                       huc.connect();
+                       if (transfer != null) {
+                               transfer.transfer(huc.getOutputStream());
+                       }
+                       // TODO other settings? There's a bunch here.
+               } catch (Exception e) {
+                       throw new APIException(e);
+               } finally { // ensure all these are reset after sends
+                       meth=pathinfo=null;
+                       if(headers!=null) {
+                               headers.clear();
+                       }
+                       pathinfo = query = fragment = "";
+               }
+       }
+       
+       public URI getURI() {
+               return uri;
+       }
+
+       public int timeout() {
+               return connectTimeout;
+       }
+       
+       public abstract class HFuture<T> extends Future<T> {
+               protected HttpURLConnection huc;
+               protected int respCode;
+               protected String respMessage;
+               protected IOException exception;
+               protected StringBuilder errContent;
+       
+               public HFuture(final HttpURLConnection huc) {
+                       this.huc = huc;
+               }
+       
+               protected boolean evalInfo(HttpURLConnection huc) throws APIException, IOException{
+                       return respCode == 200;
+               };
+       
+               @Override
+               public final boolean get(int timeout) throws CadiException {
+                       try {
+                               huc.setReadTimeout(timeout);
+                               respCode = huc.getResponseCode();
+                               ss.setLastResponse(respCode);
+                               if(evalInfo(huc)) {
+                                       return true;
+                               } else {
+                                       extractError();
+                                       return false;
+                               }
+                       } catch (IOException | APIException e) {
+                               throw new CadiException(e);
+                       } finally {
+                               close();
+                       }
+               }
+       
+               private void extractError() {
+                       InputStream is = huc.getErrorStream();
+                       try {
+                               if(is==null) {
+                                       is = huc.getInputStream();
+                               }
+                               if(is!=null) {
+                               errContent = new StringBuilder();
+                               int c;
+                                       while((c=is.read())>=0) {
+                                               errContent.append((char)c);
+                                       }
+                               }
+                       } catch (IOException e) {
+                               exception = e;
+                       }
+               }
+       
+               // Typically only used by Read
+               public StringBuilder inputStreamToString(InputStream is) {
+                       // Avoids Carriage returns, and is reasonably efficient, given
+                       // the buffer reads.
+                       try {
+                               StringBuilder sb = new StringBuilder();
+                               Reader rdr = new InputStreamReader(is);
+                               try {
+                                       char[] buf = new char[256];
+                                       int read;
+                                       while ((read = rdr.read(buf)) >= 0) {
+                                               sb.append(buf, 0, read);
+                                       }
+                               } finally {
+                                       rdr.close();
+                               }
+                               return sb;
+                       } catch (IOException e) {
+                               exception = e;
+                               return null;
+                       }
+               }
+       
+       
+               @Override
+               public int code() {
+                       return respCode;
+               }
+       
+               public HttpURLConnection huc() {
+                       return huc;
+               }
+       
+               public IOException exception() {
+                       return exception;
+               }
+       
+               public String respMessage() {
+                       return respMessage;
+               }
+       
+               @Override
+               public String header(String tag) {
+                       return huc.getHeaderField(tag);
+               }
+       
+               public void close() {
+                       if(huc!=null) {
+                               huc.disconnect();
+                       }
+               }
+       }
+
+       @Override
+       public <T> Future<T> futureCreate(Class<T> t) {
+               return new HFuture<T>(huc) {
+                       public boolean evalInfo(HttpURLConnection huc) {
+                               return respCode==201;
+                       }
+
+                       @Override
+                       public String body() {
+                               if (errContent != null) {
+                                       return errContent.toString();
+       
+                               } else if (respMessage != null) {
+                                       return respMessage;
+                               }
+                               return "";
+                       }
+               };
+       }
+
+       @Override
+       public Future<String> futureReadString() {
+               return new HFuture<String>(huc) {
+                       public boolean evalInfo(HttpURLConnection huc) throws IOException {
+                               if (respCode == 200) {
+                                       StringBuilder sb = inputStreamToString(huc.getInputStream());
+                                       if (sb != null) {
+                                               value = sb.toString();
+                                       }
+                                       return true;
+                               }
+                               return false;
+                       }
+
+                       @Override
+                       public String body() {
+                               if (value != null) {
+                                       return value;
+                               } else if (errContent != null) {
+                                       return errContent.toString();
+                               } else if (respMessage != null) {
+                                       return respMessage;
+                               }
+                               return "";
+                       }
+
+               };
+       }
+
+       @Override
+       public <T> Future<T> futureRead(final RosettaDF<T> df, final TYPE type) {
+               return new HFuture<T>(huc) {
+                       private Data<T> data;
+
+                       public boolean evalInfo(HttpURLConnection huc) throws APIException, IOException {
+                               if (respCode == 200) {
+                                       data = df.newData().in(type).load(huc.getInputStream());
+                                       value = data.asObject();
+                                       return true;
+                               }
+                               return false;
+                       }
+
+                       @Override
+                       public String body() {
+                               if (data != null) {
+                                       try {
+                                               return data.asString();
+                                       } catch (APIException e) {
+                                       }
+                               } else if (errContent != null) {
+                                       return errContent.toString();
+                               } else if (respMessage != null) {
+                                       return respMessage;
+                               }
+                               return "";
+                       }
+               };
+       }
+
+       @Override
+       public <T> Future<T> future(final T t) {
+               return new HFuture<T>(huc) {
+                       public boolean evalInfo(HttpURLConnection huc) {
+                               if (respCode == 200) {
+                                       value = t;
+                                       return true;
+                               }
+                               return false;
+                       }
+
+                       @Override
+                       public String body() {
+                               if (errContent != null) {
+                                       return errContent.toString();
+                               } else if (respMessage != null) {
+                                       return respMessage;
+                               }
+                               return Integer.toString(respCode);
+                       }
+               };
+       }
+
+       @Override
+       public Future<Void> future(final HttpServletResponse resp, final int expected) throws APIException {
+               return new HFuture<Void>(huc) {
+                       public boolean evalInfo(HttpURLConnection huc) throws IOException, APIException {
+                               resp.setStatus(respCode);
+                               int read;
+                               InputStream is;
+                               OutputStream os = resp.getOutputStream();
+                               if(respCode==expected) {
+                                       is = huc.getInputStream();
+                                       // reuse Buffers
+                                       Pooled<byte[]> pbuff = Rcli.buffPool.get();
+                                       try { 
+                                               while((read=is.read(pbuff.content))>=0) {
+                                                       os.write(pbuff.content,0,read);
+                                               }
+                                       } finally {
+                                               pbuff.done();
+                                       }
+                                       return true;
+                               } else {
+                                       is = huc.getErrorStream();
+                                       if(is==null) {
+                                               is = huc.getInputStream();
+                                       }
+                                       if(is!=null) {
+                                               errContent = new StringBuilder();
+                                               Pooled<byte[]> pbuff = Rcli.buffPool.get();
+                                               try { 
+                                                       while((read=is.read(pbuff.content))>=0) {
+                                                               os.write(pbuff.content,0,read);
+                                                       }
+                                               } finally {
+                                                       pbuff.done();
+                                               }
+                                       }
+                               }
+                               return false;
+                       }
+
+                       @Override
+                       public String body() {
+                               return errContent==null?respMessage:errContent.toString();
+                       }
+               };
+       }
+
+       private static class Header {
+               public final String tag;
+               public final String value;
+
+               public Header(String t, String v) {
+                       this.tag = t;
+                       this.value = v;
+               }
+               
+               public String toString() {
+                       return tag + '=' + value;
+               }
+       }
+       
+       public String toString() {
+               return "HttpURLConnection Client configured to " + uri.toString();
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java
new file mode 100644 (file)
index 0000000..2aa10ac
--- /dev/null
@@ -0,0 +1,248 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.SocketException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.misc.env.APIException;
+
+public class HMangr {
+       private String apiVersion;
+       private int readTimeout, connectionTimeout;
+       public final Locator<URI> loc;
+       private Access access;
+       
+       public HMangr(Access access, Locator<URI> loc) throws LocatorException {
+               readTimeout = 10000;
+               connectionTimeout=3000;
+               if(loc ==  null) {
+                       throw new LocatorException("Null Locator passed");
+               }
+               this.loc = loc;
+               this.access = access;
+       }
+
+       /**
+        * Reuse the same service.  This is helpful for multiple calls that change service side cached data so that 
+        * there is not a speed issue.
+        * 
+        * If the service goes down, another service will be substituted, if available.
+        * 
+        * @param access
+        * @param loc
+        * @param ss
+        * @param item
+        * @param retryable
+        * @return
+        * @throws URISyntaxException 
+        * @throws Exception
+        */
+       public<RET> RET same(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws APIException, CadiException, LocatorException {
+               RET ret = null;
+               boolean retry = true;
+               Rcli<HttpURLConnection> client = retryable.lastClient();
+               try {
+                       do {
+                               Item item;
+                               // if no previous state, get the best
+                               if(retryable.item()==null) {
+                                       item = loc.best();
+                                       if(item==null) {
+                                               throw new LocatorException("No Services Found for " + loc);
+                                       }
+                                       retryable.item(item);
+                                       retryable.lastClient = null;
+                               }
+                               if(client==null) {
+                                       item = retryable.item();
+                                       URI uri=loc.get(item);
+                                       if(uri==null) {
+                                               loc.invalidate(retryable.item());
+                                               if(loc.hasItems()) {
+                                                       retryable.item(loc.next(retryable.item()));
+                                                       continue;
+                                               } else {
+                                                       throw new LocatorException("No clients available for " + loc.toString());
+                                               }
+                                       }
+                                       client = new HRcli(this, uri,item,ss)
+                                               .connectionTimeout(connectionTimeout)
+                                               .readTimeout(readTimeout)
+                                               .apiVersion(apiVersion);
+                               } else {
+                                       client.setSecuritySetter(ss);
+                               }
+                               
+                               retry = false;
+                               try {
+                                       ret = retryable.code(client);
+                               } catch (APIException | CadiException e) {
+                                       item = retryable.item();
+                                       loc.invalidate(item);
+                                       retryable.item(loc.next(item));
+                                       try {
+                                               Throwable ec = e.getCause();
+                                               if(ec instanceof java.net.ConnectException) {
+                                                       if(client!=null && loc.hasItems()) { 
+                                                               access.log(Level.WARN,"Connection refused, trying next available service");
+                                                               retry = true;
+                                                       } else {
+                                                               throw new CadiException("Connection refused, no more services to try");
+                                                       }
+                                               } else if(ec instanceof java.net.SocketException) {
+                                                       if(client!=null && loc.hasItems()) { 
+                                                               access.log(Level.WARN,"Socket prematurely closed, trying next available service");
+                                                               retry = true;
+                                                       } else {
+                                                               throw new CadiException("Socket prematurely closed, no more services to try");
+                                                       }
+                                               } else if(ec instanceof SSLHandshakeException) {
+                                                       retryable.item(null);
+                                                       throw e;
+                                               } else if(ec instanceof SocketException) {
+                                                       if("java.net.SocketException: Connection reset".equals(ec.getMessage())) {
+                                                               access.log(Level.ERROR, ec.getMessage(), " can mean Certificate Expiration or TLS Protocol issues");
+                                                       }
+                                                       retryable.item(null);
+                                                       throw e;
+                                               } else {
+                                                       retryable.item(null);
+                                                       throw e;
+                                               }
+                                       } finally {
+                                               client = null;
+                                       }
+                               } catch (ConnectException e) {
+                                       item = retryable.item();
+                                       loc.invalidate(item);
+                                       retryable.item(loc.next(item));
+                               }
+                       } while(retry);
+               } finally {
+                       retryable.lastClient = client;
+               }
+               return ret;
+       }
+       
+       
+       public<RET> RET best(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+               if(loc==null) {
+                       throw new LocatorException("No Locator Configured");
+               }
+               retryable.item(loc.best());
+               return same(ss,retryable);
+       }
+       public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException {
+               return oneOf(ss,retryable,true,null);
+       }
+
+       public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify) throws LocatorException, CadiException, APIException {
+               return oneOf(ss,retryable,notify,null);
+       }
+       
+       public<RET> RET oneOf(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify,String host) throws LocatorException, CadiException, APIException {
+               RET ret = null;
+               // make sure we have all current references:
+               loc.refresh();
+               for(Item li=loc.first();li!=null;li=loc.next(li)) {
+                       URI uri=loc.get(li);
+                       if(host!=null && !host.equals(uri.getHost())) {
+                               break;
+                       }
+                       try {
+                               ret = retryable.code(new HRcli(this,uri,li,ss));
+                               access.log(Level.DEBUG,"Success calling",uri,"during call to all services");
+                       } catch (APIException | CadiException e) {
+                               Throwable t = e.getCause();
+                               if(t!=null && t instanceof ConnectException) {
+                                       loc.invalidate(li);
+                                       access.log(Level.ERROR,"Connection to",uri,"refused during call to all services");
+                               } else if(t instanceof SSLHandshakeException) {
+                                       access.log(Level.ERROR,t.getMessage());
+                                       loc.invalidate(li);
+                               } else if(t instanceof SocketException) {
+                                       if("java.net.SocketException: Connection reset".equals(t.getMessage())) {
+                                               access.log(Level.ERROR, t.getMessage(), " can mean Certificate Expiration or TLS Protocol issues");
+                                       }
+                                       retryable.item(null);
+                                       throw e;
+                               } else {
+                                       throw e;
+                               }
+                       } catch (ConnectException e) {
+                               loc.invalidate(li);
+                               access.log(Level.ERROR,"Connection to",uri,"refused during call to all services");
+                       }
+               }
+                       
+               if(ret == null && notify) 
+                       throw new LocatorException("No available clients to call");
+               return ret;
+       }
+       
+
+       public void close() {
+               // TODO Anything here?
+       }
+
+       public HMangr readTimeout(int timeout) {
+               this.readTimeout = timeout;
+               return this;
+       }
+
+       public int readTimeout() {
+               return readTimeout;
+       }
+       
+       public void connectionTimeout(int t) {
+               connectionTimeout = t;
+       }
+
+       public int connectionTimout() {
+               return connectionTimeout;
+       }
+
+       public HMangr apiVersion(String version) {
+               apiVersion = version;
+               return this;
+       }
+
+       public String apiVersion() {
+               return apiVersion;
+       }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java
new file mode 100644 (file)
index 0000000..b857f3a
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HNoAuthSS extends AbsAuthentication<HttpURLConnection> {
+       public HNoAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException {
+               super(si,"noauth",null);
+       }
+
+       @Override
+       public void setSecurity(HttpURLConnection client) throws CadiException {
+               if(securityInfo!=null && client instanceof HttpsURLConnection) {
+                       securityInfo.setSocketFactoryOn((HttpsURLConnection)client);
+               }
+       }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java
new file mode 100644 (file)
index 0000000..f289b0e
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.client.EClient;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data.TYPE;
+
+/**
+ * Rosetta Client
+ * 
+ * JAXB defined JSON or XML over HTTP/S
+ * 
+ * @author Jonathan
+ *
+ * @param <T>
+ */
+public class HRcli extends Rcli<HttpURLConnection> {
+       private HMangr hman;
+       private Item item;
+       private SecuritySetter<HttpURLConnection> ss;
+
+       public HRcli(HMangr hman, Item locItem, SecuritySetter<HttpURLConnection> secSet) throws URISyntaxException, LocatorException {
+               item=locItem;
+               uri=hman.loc.get(locItem);
+               this.hman = hman;
+               ss=secSet;
+               type = TYPE.JSON;
+               apiVersion = hman.apiVersion();
+       }
+
+       public HRcli(HMangr hman, URI uri, Item locItem, SecuritySetter<HttpURLConnection> secSet) {
+               locItem=item;
+               this.uri = uri;
+               this.hman = hman;
+               ss=secSet;
+               type = TYPE.JSON;
+               apiVersion = hman.apiVersion();
+       }
+
+       @Override
+       protected HRcli clone(URI uri, SecuritySetter<HttpURLConnection> ss) {
+               return new HRcli(hman,uri,item,ss);
+       }
+
+
+
+       /**
+        * 
+        * @return
+        * @throws APIException 
+        * @throws DME2Exception 
+        */
+       protected EClient<HttpURLConnection> client() throws CadiException {
+               try {
+                       if(uri==null) {
+                               Item item = hman.loc.best();
+                               if(item==null) {
+                                       throw new CadiException("No service available for " + hman.loc.toString());
+                               }
+                               uri = hman.loc.get(item);
+                       }
+                       return new HClient(ss,uri,connectionTimeout);
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+       }
+       
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.client.Rcli#setSecuritySetter(org.onap.aaf.cadi.SecuritySetter)
+        */
+       @Override
+       public void setSecuritySetter(SecuritySetter<HttpURLConnection> ss) {
+               this.ss = ss;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.client.Rcli#getSecuritySetter()
+        */
+       @Override
+       public SecuritySetter<HttpURLConnection> getSecuritySetter() {
+               return ss;
+       }
+
+       public void invalidate() throws CadiException {
+               try {
+                       hman.loc.invalidate(item);
+               } catch (Exception e) {
+                       throw new CadiException(e);
+               }
+       }
+       
+       public HRcli setManager(HMangr hman) {
+               this.hman = hman;
+               return this;
+       }
+
+       public String toString() {
+               return uri.toString();
+       }
+       
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java
new file mode 100644 (file)
index 0000000..873e0fe
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import org.onap.aaf.cadi.config.SecurityInfoC;
+
+public class HTokenSS extends HAuthorizationHeader {
+       public HTokenSS(final SecurityInfoC<HttpURLConnection> si, final String client_id, final String token) throws IOException {
+               super(si, client_id,"Bearer " + token);
+       }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java
new file mode 100644 (file)
index 0000000..d19c42e
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.client.AbsTransferSS;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+
+public class HTransferSS extends AbsTransferSS<HttpURLConnection> {
+       public HTransferSS(TaggedPrincipal principal, String app) throws IOException {
+               super(principal, app);
+       }
+       
+       public HTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<HttpURLConnection> si) {
+               super(principal, app, si);
+       }
+
+       @Override
+       public void setSecurity(HttpURLConnection huc) throws CadiException {
+               if(defSS==null) {
+                       throw new CadiException("Need App Credentials to send message");
+               }
+               defSS.setSecurity(huc);
+               if(value!=null) {
+                               huc.addRequestProperty(Config.CADI_USER_CHAIN, value);
+               }
+               if(securityInfo!=null) {
+                       securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+               }
+       }
+       
+       @Override
+       public int setLastResponse(int respCode) {
+               return 0;
+       }
+
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java
new file mode 100644 (file)
index 0000000..9d555f6
--- /dev/null
@@ -0,0 +1,152 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.X509KeyManager;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.SecuritySetter;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.client.AbsAuthentication;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+
+
+public class HX509SS implements SecuritySetter<HttpURLConnection> {
+       private static final byte[] X509 = "x509 ".getBytes();
+       private PrivateKey priv;
+       private byte[] pub;
+       private String cert;
+       private SecurityInfoC<HttpURLConnection> securityInfo;
+       private String algo;
+       private String alias;
+       private static int count = new SecureRandom().nextInt();
+
+       public HX509SS(SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException {
+               this(null,si,false);
+       }
+       
+       public HX509SS(SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException {
+               this(null,si,asDefault);
+       }
+       
+       public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException {
+               this(sendAlias, si, false);
+       }
+
+       public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException {
+               securityInfo = si;
+               if((alias=sendAlias) == null) {
+                       if(si.default_alias == null) {
+                               throw new APIException("JKS Alias is required to use X509SS Security.  Use " + Config.CADI_ALIAS +" to set default alias");
+                       } else {
+                               alias = si.default_alias;
+                       }
+               }
+               
+               priv=null;
+               X509KeyManager[] xkms = si.getKeyManagers();
+               if(xkms==null || xkms.length==0) {
+                       throw new APIException("There are no valid keys available in given Keystores.  Wrong Keypass?  Expired?");
+               }
+               for(int i=0;priv==null&&i<xkms.length;++i) {
+                       priv = xkms[i].getPrivateKey(alias);
+               }
+               try {
+                       for(int i=0;cert==null&&i<xkms.length;++i) {
+                               X509Certificate[] chain = xkms[i].getCertificateChain(alias);
+                               if(chain!=null&&chain.length>0) {
+                                       algo = chain[0].getSigAlgName(); 
+                                       pub = chain[0].getEncoded();
+                                       ByteArrayOutputStream baos = new ByteArrayOutputStream(pub.length*2); 
+                                       ByteArrayInputStream bais = new ByteArrayInputStream(pub);
+                                       Symm.base64noSplit.encode(bais,baos,X509);
+                                       cert = baos.toString();
+                               }
+                       }
+               } catch (CertificateEncodingException | IOException e) {
+                       throw new CadiException(e);
+               }
+               if(algo==null) {
+                       throw new APIException("X509 Security Setter not configured");
+               }
+       }
+
+       @Override
+       public void setSecurity(HttpURLConnection huc) throws CadiException {
+               if(huc instanceof HttpsURLConnection) {
+                       securityInfo.setSocketFactoryOn((HttpsURLConnection)huc);
+               }
+               if(alias==null) { // must be a one-way
+                       huc.setRequestProperty(AbsAuthentication.AUTHORIZATION, cert);
+                       
+                       // Test Signed content
+                       try {
+                               String data = "SignedContent["+ inc() + ']' + Chrono.dateTime();
+                               huc.setRequestProperty("Data", data);
+                               
+                               Signature sig = Signature.getInstance(algo);
+                               sig.initSign(priv);
+                               sig.update(data.getBytes());
+                               byte[] signature = sig.sign();
+                               
+                               ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(signature.length*1.3));
+                               ByteArrayInputStream bais = new ByteArrayInputStream(signature);
+                               Symm.base64noSplit.encode(bais, baos);
+                               huc.setRequestProperty("Signature", new String(baos.toByteArray()));
+                               
+                       } catch (Exception e) {
+                               throw new CadiException(e);
+                       }
+               }
+       }
+       
+       private synchronized int inc() {
+               return ++count;
+       }
+       
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.SecuritySetter#getID()
+        */
+       @Override
+       public String getID() {
+               return alias;
+       }
+       
+       @Override
+       public int setLastResponse(int respCode) {
+               return 0;
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java
new file mode 100644 (file)
index 0000000..655a0c2
--- /dev/null
@@ -0,0 +1,210 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.locator;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+
+public class DNSLocator implements Locator<URI> {
+       private static enum Status {UNTRIED, OK, INVALID, SLOW};
+       private static final int CHECK_TIME = 3000;
+       
+       private String host, protocol;
+       private Access access;
+       private Host[] hosts;
+       private int startPort, endPort;
+       private String suffix;
+       
+       public DNSLocator(Access access, String protocol, String host, String range) {
+               this.host = host;
+               this.protocol = protocol;
+               this.access = access;
+               int dash = range.indexOf('-');
+               if(dash<0) {
+                       startPort = endPort = Integer.parseInt(range);
+               } else {
+                       startPort = Integer.parseInt(range.substring(0,dash));
+                       endPort = Integer.parseInt(range.substring(dash + 1));
+               }
+               refresh();
+       }
+
+       public DNSLocator(Access access, String aaf_locate) throws LocatorException {
+               this.access = access;
+               if(aaf_locate==null) {
+                       throw new LocatorException("Null passed into DNSLocator constructor");
+               }
+               int start, port;
+               if(aaf_locate.startsWith("https:")) {
+                       protocol = "https:";
+                       start = 9; // https://
+                       port = 443;
+               } else if(aaf_locate.startsWith("http:")) {
+                       protocol = "http:";
+                       start = 8; // http://
+                       port = 80;
+               } else {
+                       throw new LocatorException("DNSLocator accepts only https or http protocols.  (requested URL " + aaf_locate + ')');
+               }
+               
+               int colon = aaf_locate.indexOf(':',start);
+               int slash;
+               if(colon>0) {
+                       start = colon+1;
+                       int left = aaf_locate.indexOf('[',start);
+                       if(left>0) {
+                               int right = aaf_locate.indexOf(']',left+1);
+                               if(right>0) {
+                                       int dash = aaf_locate.indexOf('-',left+1);
+                                       if(dash<0) {
+                                               startPort = endPort = Integer.parseInt(aaf_locate.substring(left+1,right));
+                                       } else {
+                                               startPort = Integer.parseInt(aaf_locate.substring(left+1,dash));
+                                               endPort = Integer.parseInt(aaf_locate.substring(dash + 1,right));
+                                       }
+                               }
+                               
+                       } else {
+                               slash = aaf_locate.indexOf('/',colon+1);
+                               if(slash<0) {
+                                       startPort = endPort = Integer.parseInt(aaf_locate.substring(start));
+                               } else {
+                                       startPort = endPort = Integer.parseInt(aaf_locate.substring(start,slash));
+                               }
+                       }
+               } else {
+                       startPort = endPort = port;
+               }               
+       }
+
+       @Override
+       public URI get(Item item) throws LocatorException {
+               return hosts[((DLItem)item).cnt].uri;
+       }
+
+       @Override
+       public boolean hasItems() {
+               for(Host h : hosts) {
+                       if(h.status==Status.OK) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       @Override
+       public void invalidate(Item item) {
+               DLItem di = (DLItem)item;
+               hosts[di.cnt].status = Status.INVALID;
+       }
+
+       @Override
+       public Item best() throws LocatorException {
+               // not a good "best"
+               for(int i=0;i<hosts.length;++i) {
+                       switch(hosts[i].status) {
+                               case OK:
+                                       return new DLItem(i);
+                               case INVALID:
+                                       break;
+                               case SLOW:
+                                       break;
+                               case UNTRIED:
+                                       try {
+                                               if(hosts[i].ia.isReachable(CHECK_TIME)) {
+                                                       hosts[i].status = Status.OK;
+                                                       return new DLItem(i);
+                                               }
+                                       } catch (IOException e) {
+                                               throw new LocatorException(e);
+                                       }
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               throw new LocatorException("No Available URIs for " + host);
+       }
+
+       @Override
+       public Item first() throws LocatorException {
+               return new DLItem(0);
+       }
+
+       @Override
+       public Item next(Item item) throws LocatorException {
+               DLItem di = (DLItem)item;
+               if(++di.cnt<hosts.length) {
+                       return di;
+               } else {
+                       return null;
+               }
+       }
+
+       @Override
+       public boolean refresh() {
+               try {
+                       InetAddress[] ias = InetAddress.getAllByName(host);
+                       Host[] temp = new Host[ias.length * (1 + endPort - startPort)];
+                       int cnt = -1;
+                       for(int j=startPort; j<=endPort; ++j) {
+                               for(int i=0;i<ias.length;++i) {
+                                       temp[++cnt] = new Host(ias[i], j, suffix);
+                               }
+                       }
+                       hosts = temp;
+                       return true;
+               } catch (Exception e) {
+                       access.log(Level.ERROR, e);
+               }
+               return false;
+       }
+
+       private class Host {
+               private URI uri;
+               private InetAddress ia;
+               private Status status;
+               
+               public Host(InetAddress inetAddress, int port, String suffix) throws URISyntaxException {
+                       ia = inetAddress;
+                       uri = new URI(protocol,null,inetAddress.getHostAddress(),port,suffix,null,null);
+                       status = Status.UNTRIED;
+               }
+       }
+       
+       private class DLItem implements Item {
+               public DLItem(int i) {
+                       cnt = i;
+               }
+
+               private int cnt;
+       }
+       
+       public void destroy() {}
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java
new file mode 100644 (file)
index 0000000..b97768a
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.locator;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.http.HClient;
+import org.onap.aaf.cadi.http.HX509SS;
+
+public class HClientHotPeerLocator extends HotPeerLocator<HClient> {
+       private final HX509SS ss;
+
+       public HClientHotPeerLocator(Access access, String urlstr, long invalidateTime, String localLatitude,
+                       String localLongitude, HX509SS ss) throws LocatorException {
+               super(access, urlstr, invalidateTime, localLatitude, localLongitude);
+               
+               this.ss = ss;
+       }
+
+       @Override
+       protected HClient _newClient(String clientInfo) throws LocatorException {
+               try {
+                       int idx = clientInfo.indexOf('/');
+                       return new HClient(ss,new URI("https://"+(idx<0?clientInfo:clientInfo.substring(0, idx))),3000);
+               } catch (URISyntaxException e) {
+                       throw new LocatorException(e);
+               }
+       }
+
+       @Override
+       protected HClient _invalidate(HClient client) {
+               return null;
+       }
+
+       @Override
+       protected void _destroy(HClient client) {
+       }
+}
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java
new file mode 100644 (file)
index 0000000..17f9baf
--- /dev/null
@@ -0,0 +1,302 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.locator;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.routing.GreatCircle;
+import org.onap.aaf.misc.env.util.Split;
+
+/**
+ * This Locator is to handle Hot Peer load protection, when the Servers are 
+ *     1) Static
+ *     2) Well known client URL
+ * 
+ * The intention is to change traffic over to the Hot Peer, if a server goes down, and reinstate
+ * when it is back up.
+ * 
+ * Example of this kind of Service is a MS Certificate Server
+ * 
+ * @author Jonathan
+ *
+ * @param <CLIENT>
+ */
+public abstract class HotPeerLocator<CLIENT> implements Locator<CLIENT> {
+               private final String[] urlstrs;
+               private final CLIENT[] clients;
+               private final long[] failures;
+               private final double[] distances;
+               private int preferred;
+               private long invalidateTime;
+               private Thread refreshThread;
+               protected Access access;
+
+               /**
+                * Construct:  Expect one or more Strings in the form:
+                *              192.555.112.223:39/38.88087/-77.30122
+                *    separated by commas
+                * 
+                * @param trans
+                * @param urlstr
+                * @param invalidateTime
+                * @param localLatitude
+                * @param localLongitude
+                * @throws LocatorException
+                */
+               @SuppressWarnings("unchecked")
+               protected HotPeerLocator(Access access, final String urlstr, final long invalidateTime, final String localLatitude, final String localLongitude) throws LocatorException {
+                       this.access = access;
+                       urlstrs = Split.split(',', urlstr);
+                       clients = (CLIENT[])new Object[urlstrs.length];
+                       failures = new long[urlstrs.length];
+                       distances= new double[urlstrs.length];
+                       this.invalidateTime = invalidateTime;
+                       
+                       double distance = Double.MAX_VALUE;
+                       for(int i=0;i<urlstrs.length;++i) {
+                               String[] info = Split.split('/', urlstrs[i]);
+                               if(info.length<3) {
+                                       throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");
+                               }
+                               try {
+                                       clients[i] = _newClient(urlstrs[i]);
+                                       failures[i] = 0L;
+                               } catch(LocatorException le) {
+                                       failures[i] = System.currentTimeMillis()+invalidateTime;
+                               }
+                               
+                               double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude);
+                               distances[i]=d;
+                               
+                               // find preferred server
+                               if(d<distance) {
+                                       preferred = i;
+                                       distance=d;
+                               }
+                       }
+                       
+                       access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]);
+                       for(int i=0;i<urlstrs.length;++i) {
+                               if(i!=preferred) {
+                                       access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]);
+                               }
+                       }
+               }
+               
+               protected abstract CLIENT _newClient(String hostInfo) throws LocatorException;
+               /**
+                * If client can reconnect, then return.  Otherwise, destroy and return null;
+                * @param client
+                * @return
+                * @throws LocatorException
+                */
+               protected abstract CLIENT _invalidate(CLIENT client);
+               
+               protected abstract void _destroy(CLIENT client);
+               
+               @Override
+               public Item best() throws LocatorException {
+                       if(failures[preferred]==0L) {
+                               return new HPItem(preferred);
+                       } else {
+                               long now = System.currentTimeMillis();
+                               double d = Double.MAX_VALUE;
+                               int best = -1;
+                               boolean tickle = false;
+                               // try for best existing client
+                               for(int i=0;i<urlstrs.length;++i) {
+                                       if(failures[i]<now && distances[i]<d) {
+                                               if(clients[i]!=null) {
+                                                       best = i;
+                                                       break;
+                                               } else {
+                                                       tickle = true; // There's some failed clients which can be restored
+                                               }
+                                       }
+                               }
+                               if(best<0 && tickle) {
+                                       tickle=false;
+                                       if(refresh()) {
+                                               // try again
+                                               for(int i=0;i<urlstrs.length;++i) {
+                                                       if(failures[i]==0L && distances[i]<d) {
+                                                               if(clients[i]!=null) {
+                                                                       best = i;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       
+                               /*
+                                * If a valid client is available, but there are some that can refresh, return the client immediately
+                                * but start a Thread to do the background Client setup.
+                                */
+                               if(tickle) {
+                                       synchronized(clients) {
+                                               if(refreshThread==null) {
+                                                       refreshThread = new Thread(new Runnable(){
+                                                               @Override
+                                                               public void run() {
+                                                                       refresh();
+                                                                       refreshThread = null;
+                                                               }
+                                                       });
+                                                       refreshThread.setDaemon(true);
+                                                       refreshThread.start();
+                                               }
+                                       }
+                               }
+                               
+                               if(best<0) {
+                                       throw new LocatorException("No Clients available");
+                               }
+
+                               
+                               return new HPItem(best);
+                       }
+               }
+               
+
+               @Override
+               public CLIENT get(Item item) throws LocatorException {
+                       HPItem hpi = (HPItem)item;
+                       CLIENT c = clients[hpi.idx];
+                       if(c==null) {
+                               if(failures[hpi.idx]>System.currentTimeMillis()) {
+                                       throw new LocatorException("Client requested is invalid");      
+                               } else {
+                                       synchronized(clients) {
+                                               c = _newClient(urlstrs[hpi.idx]);
+                                               failures[hpi.idx]=0L;
+                                       }
+                               }
+                       } else if(failures[hpi.idx]>0){
+                               throw new LocatorException("Client requested is invalid");
+                       }
+                       return c;
+               }
+               
+               public String info(Item item) {
+                       HPItem hpi = (HPItem)item;
+                       if(hpi!=null && hpi.idx<urlstrs.length) {
+                               return urlstrs[hpi.idx];
+                       } else {
+                               return "Invalid Item";
+                       }
+               }
+
+               @Override
+               public boolean hasItems() {
+                       for(int i=0;i<clients.length;++i) {
+                               if(clients[i]!=null && failures[i]==0L) {
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+               
+               @Override
+               public synchronized void invalidate(Item item) throws LocatorException {
+                       HPItem hpi = (HPItem)item;
+                       failures[hpi.idx] = System.currentTimeMillis() + invalidateTime;
+                       CLIENT c = clients[hpi.idx];
+                       clients[hpi.idx] = _invalidate(c);
+               }
+               
+               @Override
+               public Item first() throws LocatorException {
+                       return new HPItem(0);
+               }
+               
+               @Override
+               public Item next(Item item) throws LocatorException {
+                       HPItem hpi = (HPItem)item;
+                       if(++hpi.idx>=clients.length) {
+                               return null;
+                       }
+                       return hpi;
+               }
+               
+               @Override
+               public boolean refresh() {
+                       boolean force = !hasItems(); // If no Items at all, reset
+                       boolean rv = true;
+                       long now = System.currentTimeMillis();
+                       for(int i=0;i<clients.length;++i) {
+                               if(failures[i]>0L && (failures[i]<now || force)) { // retry
+                                       try {
+                                               synchronized(clients) {
+                                                       if(clients[i]==null) {
+                                                               clients[i]=_newClient(urlstrs[i]);
+                                                       }
+                                                       failures[i]=0L;
+                                               }
+                                       } catch (LocatorException e) {
+                                               failures[i]=now+invalidateTime;
+                                               rv = false;
+                                       }
+                               }
+                       }
+                       return rv;
+               }
+               
+               @Override
+               public void destroy() {
+                       for(int i=0;i<clients.length;++i) {
+                               if(clients[i]!=null) {
+                                       _destroy(clients[i]);
+                                       clients[i] = null;
+                               }
+                       }
+               }
+
+               private static class HPItem implements Item {
+                       private int idx;
+
+                       public HPItem(int i) {
+                               idx = i;
+                       }
+               }
+               
+
+               /*
+                * Convenience Functions
+                */
+               public CLIENT bestClient() throws LocatorException {
+                       return get(best());
+               }
+
+               public boolean invalidate(CLIENT client) throws LocatorException {
+                       for(int i=0;i<clients.length;++i) {
+                               if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference
+                                       invalidate(new HPItem(i));
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+
+       }
\ No newline at end of file
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java
new file mode 100644 (file)
index 0000000..244a43b
--- /dev/null
@@ -0,0 +1,289 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.locator;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.misc.env.util.Split;
+
+public class PropertyLocator implements Locator<URI> {
+       private final URI [] orig;
+       private PLItem[] current;
+       private int end;
+       private final SecureRandom random;
+       private URI[] resolved;
+       private long lastRefreshed;
+       private long minRefresh;
+       private long backgroundRefresh;
+
+       public PropertyLocator(String locList) throws LocatorException {
+               this(locList,10000L, 1000*60*20); // defaults, do not refresh more than once in 10 seconds, Refresh Locator every 20 mins.
+       }
+       /**
+        * comma delimited root url list
+        * 
+        * @param locList
+        * @throws LocatorException
+        */
+       public PropertyLocator(String locList, long minRefreshMillis, long backgroundRefreshMillis) throws LocatorException {
+               minRefresh = minRefreshMillis;
+               backgroundRefresh = backgroundRefreshMillis;
+               lastRefreshed=0L;
+               if(locList==null) {
+                       throw new LocatorException("No Location List given for PropertyLocator");
+               }
+               String[] locarray = Split.split(',',locList);
+               List<URI> uriList = new ArrayList<URI>();
+               
+               random = new SecureRandom();
+               
+               for(int i=0;i<locarray.length;++i) {
+                       try {
+                               int range = locarray[i].indexOf(":[");
+                               if(range<0) {
+                                       uriList.add(new URI(locarray[i]));
+                               } else {
+                                       String mach_colon = locarray[i].substring(0, range+1);
+                                       int dash = locarray[i].indexOf('-',range+2);
+                                       int brac = locarray[i].indexOf(']',dash+1);
+                                       int slash = locarray[i].indexOf('/',brac);
+                                       int start = Integer.parseInt(locarray[i].substring(range+2, dash));
+                                       int end = Integer.parseInt(locarray[i].substring(dash+1, brac));
+                                       for(int port=start;port<=end;++port) {
+                                               uriList.add(new URI(mach_colon+port + (slash>=0?locarray[i].substring(slash):"")));
+                                       }
+                               }
+                       } catch (NumberFormatException nf) {
+                               throw new LocatorException("Invalid URI format: " + locarray[i]);
+                       } catch (URISyntaxException e) {
+                               throw new LocatorException(e);
+                       }
+               }
+               orig = new URI[uriList.size()];
+               uriList.toArray(orig);
+
+               refresh();
+               new Timer("PropertyLocator Refresh Timer",true).scheduleAtFixedRate(new TimerTask() {
+                       @Override
+                       public void run() {
+                               refresh();
+                       }
+               }, backgroundRefresh,backgroundRefresh);
+       }
+
+       @Override
+       public URI get(Item item) throws LocatorException {
+               synchronized(orig) {
+                       if(item==null) {
+                               return null;
+                       } else {
+                               return resolved[((PLItem)item).idx];
+                       }
+               }
+       }
+
+       @Override
+       public Item first() throws LocatorException {
+               return end>0?current[0]:null;
+       }
+
+       @Override
+       public boolean hasItems() {
+               return end>0;
+       }
+
+       @Override
+       public Item next(Item item) throws LocatorException {
+               if(item==null) {
+                       return null;
+               } else {
+                       int spot;
+                       if((spot=(((PLItem)item).order+1))>=end)return null;
+                       return current[spot];
+               }
+       }
+
+       @Override
+       public synchronized void invalidate(Item item) throws LocatorException {
+               if(--end<=0) {
+                       refresh();
+                       return;
+               }
+               if(item==null) {
+                       return;
+               }
+               PLItem pli = (PLItem)item;
+               int i,order;
+               for(i=0;i<end;++i) {
+                       if(pli==current[i])break;
+               }
+               order = current[i].order;
+               for(;i<end;++i) {
+                       current[i]=current[i+1];
+                       current[i].order=order++;
+               }
+               current[end]=pli;
+       }
+
+       @Override
+       public Item best() throws LocatorException {
+               if(current.length==0) {
+                       refresh();
+               }
+               switch(current.length) {
+                       case 0:
+                               return null;
+                       case 1:
+                               return current[0];
+                       default:
+                               return current[Math.abs(random.nextInt())%current.length];
+               }
+       }
+
+       @Override
+       public synchronized boolean refresh() {
+               if(System.currentTimeMillis()>lastRefreshed) {
+                       // Build up list
+                       List<URI> resolve = new ArrayList<URI>();
+                       String realname;
+                       for(int i = 0; i < orig.length ; ++i) {
+                               try {
+                                       InetAddress ia[] = InetAddress.getAllByName(orig[i].getHost());
+
+                                       URI o,n;
+                                       for(int j=0;j<ia.length;++j) {
+                                               o = orig[i];
+                                               Socket socket = new Socket();
+                                               try {
+                                                       realname=ia[j].getHostAddress().equals(ia[j].getHostName())?ia[j].getCanonicalHostName():ia[j].getHostName();
+                                                       int port = o.getPort();
+                                                       if(port<0) { // default
+                                                               port = "https".equalsIgnoreCase(o.getScheme())?443:80;
+                                                       }
+                                                       socket.connect(new InetSocketAddress(realname,port),3000);
+                                                       if(socket.isConnected()) {
+                                                               n = new URI(
+                                                                               o.getScheme(),
+                                                                               o.getUserInfo(),
+                                                                               realname,
+                                                                               o.getPort(),
+                                                                               o.getPath(),
+                                                                               o.getQuery(),
+                                                                               o.getFragment()
+                                                                               );
+                                                               resolve.add(n);
+                                                       }
+                                               } catch (IOException e) {
+                                               } finally {
+                                                       if(!socket.isClosed()) {
+                                                               try {
+                                                                       socket.close();
+                                                               } catch (IOException e) {
+                                                                       // nothing to do.
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } catch (UnknownHostException | URISyntaxException e) {
+                                       // Note: Orig Name already known as valid, based on constructor
+                               }
+                       }
+                       end=resolve.size();
+                       PLItem[] newCurrent;
+                       if(current==null || current.length!=end) {
+                               newCurrent = new PLItem[end];
+                       } else {
+                               newCurrent = current;
+                       }
+       
+                       for(int i=0; i< end; ++i) {
+                               if(newCurrent[i]==null){
+                                       newCurrent[i]=new PLItem(i);
+                               } else {
+                                       newCurrent[i].idx=newCurrent[i].order=i;
+                               }
+                       }
+                       synchronized(orig) {
+                               resolved = new URI[end];
+                               resolve.toArray(resolved);
+                               current = newCurrent;
+                       }
+                       lastRefreshed = System.currentTimeMillis()+minRefresh;
+                       return !resolve.isEmpty();
+               } else {
+                       return false;
+               }
+       }
+       
+       private class PLItem implements Item {
+               public int idx,order;
+               
+               public PLItem(int i) {
+                       idx = order =i;
+               }
+               
+               public String toString() {
+                       return "Item: " + idx + " order: " + order;
+               }
+       }
+
+       public String toString() {
+               StringBuilder sb = new StringBuilder();
+               boolean first = true;
+               for(URI uri : orig) {
+                       boolean isResolved=false;
+                       if(uri!=null) {
+                               if(first) {
+                                       first = false;
+                               } else {
+                                       sb.append(", ");
+                               }
+                               sb.append(uri.toString());
+                               sb.append(" [");
+                               for(URI u2 : resolved) {
+                                       if(uri.equals(u2)) {
+                                               isResolved = true;
+                                               break;
+                                       }
+                               }
+                               sb.append(isResolved?"X]\n":" ]");
+                       }
+               }
+               return sb.toString();
+       }
+       
+       public void destroy() {
+       }
+}
diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java b/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java
new file mode 100644 (file)
index 0000000..3690618
--- /dev/null
@@ -0,0 +1,188 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.routing;
+
+import org.onap.aaf.misc.env.util.Split;
+
+public class GreatCircle {
+       // Note: multiplying by this constant is faster than calling Math equivalent function 
+       private static final double DEGREES_2_RADIANS = Math.PI/180.0;
+       
+       public static final double DEGREES_2_NM = 60;
+       public static final double DEGREES_2_KM = DEGREES_2_NM * 1.852; // 1.852 is exact ratio per 1929 Standard Treaty, adopted US 1954
+       public static final double DEGREES_2_MI = DEGREES_2_NM * 1.1507795; 
+       
+       /**
+        * 
+        * Calculate the length of an arc on a perfect sphere based on Latitude and Longitudes of two points
+        *    Parameters are in Degrees (i.e. the coordinate system you get from GPS, Mapping WebSites, Phones, etc)
+        *    
+        *              L1 = Latitude of point A
+        *      G1 = Longitude of point A
+        *          L2 = Latitude of point B
+        *      G2 = Longitude of point B
+        *      
+        *      d  = acos (sin(L1)*sin(L2) + cos(L1)*cos(L2)*cos(G1 - G2))
+        * 
+        * Returns answer in Degrees
+        * 
+        * Since there are 60 degrees per nautical miles, you can convert to NM by multiplying by 60
+        * 
+        * Essential formula from a Princeton website, the "Law of Cosines" method.  
+        * 
+        * Refactored cleaned up for speed Jonathan 3/8/2013
+        * 
+        * @param latA
+        * @param lonA
+        * @param latB
+        * @param lonB
+        * @return
+        */
+       public static double calc(double latA, double lonA, double latB, double lonB) {
+               // Formula requires Radians.  Expect Params to be Coordinates (Degrees)
+               // Simple ratio, quicker than calling Math.toRadians()
+               latA *= DEGREES_2_RADIANS;
+               lonA *= DEGREES_2_RADIANS;
+               latB *= DEGREES_2_RADIANS;
+               lonB *= DEGREES_2_RADIANS;
+
+               return Math.acos(
+                               Math.sin(latA) * Math.sin(latB) + 
+                               Math.cos(latA) * Math.cos(latB) * Math.cos(lonA-lonB)
+                       )
+                       / DEGREES_2_RADIANS;
+       }
+       
+       /** 
+        * Convert from "Lat,Long Lat,Long" String format
+        *              "Lat,Long,Lat,Long" Format
+        *           or all four entries "Lat Long Lat Long"
+        * 
+        * (Convenience function)
+        * 
+        * Since Distance is positive, a "-1" indicates an error in String formatting
+        */
+       public static double calc(String ... coords) {
+               try {
+                       String [] array;
+                       switch(coords.length) {
+                       case 1:
+                               array = Split.split(',',coords[0]);
+                               if(array.length!=4)return -1;
+                               return calc(
+                                       Double.parseDouble(array[0]),
+                                       Double.parseDouble(array[1]),
+                                       Double.parseDouble(array[2]),
+                                       Double.parseDouble(array[3])
+                                       );
+                       case 2:
+                               array = Split.split(',',coords[0]);
+                               String [] array2 = Split.split(',',coords[1]);
+                               if(array.length!=2 || array2.length!=2)return -1;
+                               return calc(
+                                       Double.parseDouble(array[0]),
+                                       Double.parseDouble(array[1]),
+                                       Double.parseDouble(array2[0]),
+                                       Double.parseDouble(array2[1])
+                                       );
+                       case 4:
+                               return calc(
+                                       Double.parseDouble(coords[0]),
+                                       Double.parseDouble(coords[1]),
+                                       Double.parseDouble(coords[2]),
+                                       Double.parseDouble(coords[3])
+                                       );
+                               
+                       default:
+                               return -1;
+                       }
+               } catch (NumberFormatException e) {
+                       return -1;
+               }
+       }
+
+}
+
+///**
+//* Haverside method, from Princeton
+//* 
+//* @param alat
+//* @param alon
+//* @param blat
+//* @param blon
+//* @return
+//*/
+//public static double calc3(double alat, double alon, double blat, double blon) {
+//     alat *= DEGREES_2_RADIANS;
+//     alon *= DEGREES_2_RADIANS;
+//     blat *= DEGREES_2_RADIANS;
+//     blon *= DEGREES_2_RADIANS;
+//     return 2 * Math.asin(
+//                     Math.min(1, Math.sqrt(
+//                             Math.pow(Math.sin((blat-alat)/2), 2) +
+//                             (Math.cos(alat)*Math.cos(blat)*
+//                                     Math.pow(
+//                                             Math.sin((blon-alon)/2),2)
+//                                     )
+//                             )
+//                     )
+//             )
+//     / DEGREES_2_RADIANS;
+//}
+//
+
+
+
+//This is a MEAN radius.  The Earth is not perfectly spherical
+//     public static final double EARTH_RADIUS_KM = 6371.0;
+//     public static final double EARTH_RADIUS_NM = 3440.07;
+//     public static final double KM_2_MILES_RATIO = 0.621371192;
+///**
+//* Code on Internet based on Unknown book.  Lat/Long is in Degrees
+//* @param alat
+//* @param alon
+//* @param blat
+//* @param blon
+//* @return
+//*/
+//public static double calc1(double alat, double alon, double blat, double blon) {
+//     alat *= DEGREES_2_RADIANS;
+//     alon *= DEGREES_2_RADIANS;
+//     blat *= DEGREES_2_RADIANS;
+//     blon *= DEGREES_2_RADIANS;
+//     
+//     // Reused values
+//     double cosAlat,cosBlat;
+//     
+//     return Math.acos(
+//             ((cosAlat=Math.cos(alat))*Math.cos(alon)*(cosBlat=Math.cos(blat))*Math.cos(blon)) +
+//             (cosAlat*Math.sin(alon)*cosBlat*Math.sin(blon)) +
+//             (Math.sin(alat)*Math.sin(blat))
+//             )/DEGREES_2_RADIANS;
+//     
+//}
+
+/*
+*  This method was 50% faster than calculation 1, and 75% than the Haverside method
+*  Also, since it's based off of Agree standard Degrees of the Earth, etc, the calculations are more exact,
+*    at least for Nautical Miles and Kilometers
+*/
diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_DNSLocator.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_DNSLocator.java
new file mode 100644 (file)
index 0000000..b97667d
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client.test;
+
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.locator.DNSLocator;
+
+public class JU_DNSLocator {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               
+               DNSLocator dl = new DNSLocator(new PropAccess(), "https", "aaf.it.att.com","8150-8152");
+               try {
+                       Item item = dl.best();
+                       URI uri = dl.get(item);
+                       URL url = uri.toURL();
+                       URLConnection conn = url.openConnection();
+                       conn.connect();
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_GreatCircle.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_GreatCircle.java
new file mode 100644 (file)
index 0000000..d2e02cb
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client.test;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.routing.GreatCircle;
+
+public class JU_GreatCircle {
+
+       @BeforeClass
+       public static void setUpBeforeClass() throws Exception {
+       }
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               Assert.assertEquals(7.803062505568182,GreatCircle.calc(38.627345,-90.193774, 35.252234,-81.384929),0.000000001);
+               Assert.assertEquals(0.0,GreatCircle.calc(38.627345,-90.193774, 38.627345,-90.193774),0.000000001);
+               Assert.assertEquals(7.803062505568182,GreatCircle.calc(35.252234,-81.384929,38.627345,-90.193774),0.000000001);
+               Assert.assertEquals(7.803062505568182,GreatCircle.calc(38.627345,-90.193774, 35.252234,-81.384929),0.000000001);
+               Assert.assertEquals(7.803062505568182,GreatCircle.calc(-38.627345,90.193774, -35.252234,81.384929),0.000000001);
+               Assert.assertEquals(105.71060033936052,GreatCircle.calc(-38.627345,90.193774, -35.252234,-81.384929),0.000000001);
+               Assert.assertEquals(105.71060033936052,GreatCircle.calc(38.627345,-90.193774, 35.252234,81.384929),0.000000001);
+               Assert.assertEquals(74.32786874922931,GreatCircle.calc(-38.627345,90.193774, 35.252234,81.384929),0.000000001);
+       }
+
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_PropertyLocator.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_PropertyLocator.java
new file mode 100644 (file)
index 0000000..94f0579
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.client.test;
+
+import java.net.URI;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+
+import static org.junit.Assert.*;
+
+public class JU_PropertyLocator {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() throws Exception {
+               PropertyLocator pl = new PropertyLocator("https://localhost:2345,https://fred.wilma.com:26444,https://tom.jerry.com:534");
+               
+               Item i;
+               int count;
+               boolean print = false;
+               for(int j=0;j<900000;++j) {
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+                       assertEquals(3,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(2,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(1,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(0,count);
+                       assertFalse(pl.hasItems());
+                       
+                       pl.refresh();
+               }
+       }
+
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_DNSLocator.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_DNSLocator.java
new file mode 100644 (file)
index 0000000..777b4f9
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.client.test;
+
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.locator.DNSLocator;
+
+public class JU_DNSLocator {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() {
+               
+               DNSLocator dl = new DNSLocator(new PropAccess(), "https", "aaf.it.att.com","8150-8152");
+               try {
+                       Item item = dl.best();
+                       URI uri = dl.get(item);
+                       URL url = uri.toURL();
+                       URLConnection conn = url.openConnection();
+                       conn.connect();
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_HolderTest.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_HolderTest.java
new file mode 100644 (file)
index 0000000..51d7dd1
--- /dev/null
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.client.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.client.Holder;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class JU_HolderTest {
+
+       @Test
+       public void testSet() {
+               Holder<String> holder = new Holder<String>("StringHolder");
+               assertEquals(holder.get(), "StringHolder");
+               
+               holder.set("New String");
+               assertEquals(holder.get(), "New String");
+       }
+
+       @Test
+       public void testSet4() {
+               Holder<String> holder = new Holder<String>("StringHolder");
+               assertEquals(holder.get(), "StringHolder");
+               
+               holder.set("New String1");
+               assertEquals(holder.get(), "New String1");
+       }
+       @Test
+       public void testSet1() {
+               Holder<String> holder = new Holder<String>("StringHolder");
+               assertEquals(holder.get(), "StringHolder");
+               
+               holder.set("New String2");
+               assertEquals(holder.get(), "New String2");
+       }
+       
+       @Test
+       public void testSet2() {
+               Holder<String> holder = new Holder<String>("StringHolder");
+               assertEquals(holder.get(), "StringHolder");
+               
+               holder.set("New String3");
+               assertEquals(holder.get(), "New String3");
+       }
+       
+       @Test
+       public void testSet3() {
+               Holder<String> holder = new Holder<String>("StringHolder");
+               assertEquals(holder.get(), "StringHolder");
+               
+               holder.set("New String4");
+               assertEquals(holder.get(), "New String4");
+       }
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_PropertyLocator.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_PropertyLocator.java
new file mode 100644 (file)
index 0000000..2b3793c
--- /dev/null
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.client.test;
+
+import java.net.URI;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.locator.PropertyLocator;
+
+import static org.junit.Assert.*;
+
+public class JU_PropertyLocator {
+
+       @AfterClass
+       public static void tearDownAfterClass() throws Exception {
+       }
+
+       @Test
+       public void test() throws Exception {
+               PropertyLocator pl = new PropertyLocator("https://localhost:2345,https://fred.wilma.com:26444,https://tom.jerry.com:534");
+               
+               Item i;
+               int count;
+               boolean print = false;
+               for(int j=0;j<900000;++j) {
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+                       assertEquals(3,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(2,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(1,count);
+                       assertTrue(pl.hasItems());
+                       if(print)System.out.println("---");
+                       pl.invalidate(pl.best());
+                       
+                       count = 0;
+                       for(i = pl.first();i!=null;i=pl.next(i)) {
+                               URI loc = pl.get(i);
+                               if(print)System.out.println(loc.toString());
+                               ++count;
+                       }
+       
+                       assertEquals(0,count);
+                       assertFalse(pl.hasItems());
+                       
+                       pl.refresh();
+               }
+       }
+
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java
new file mode 100644 (file)
index 0000000..b0ac5a0
--- /dev/null
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.client.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aaf.cadi.client.Result;
+
+public class JU_ResultTest {
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @Test
+       public void testOk() {
+               Result<String> t = Result.ok(1, "Ok");
+               assertNotNull(t);
+               assertThat(t.code, is(1));
+               assertTrue(t.isOK());
+               assertThat(t.toString(), is("Code: 1"));
+       }
+
+       @Test
+       public void testErr() {
+               Result<String> t = Result.err(1, "Error Body");
+               assertNotNull(t);
+               assertThat(t.error, is("Error Body"));
+               assertFalse(t.isOK());
+               assertThat(t.toString(), is("Code: 1 = Error Body"));
+       }
+       
+       @Test
+       public void testOk1() {
+               Result<String> t = Result.ok(1, "Ok");
+               assertNotNull(t);
+               assertThat(t.code, is(1));
+               assertTrue(t.isOK());
+               assertThat(t.toString(), is("Code: 1"));
+       }
+
+       @Test
+       public void testErr1() {
+               Result<String> t = Result.err(1, "Error Body");
+               assertNotNull(t);
+               assertThat(t.error, is("Error Body"));
+               assertFalse(t.isOK());
+               assertThat(t.toString(), is("Code: 1 = Error Body"));
+       }
+       
+       @Test
+       public void testOk2() {
+               Result<String> t = Result.ok(1, "Ok");
+               assertNotNull(t);
+               assertThat(t.code, is(1));
+               assertTrue(t.isOK());
+               assertThat(t.toString(), is("Code: 1"));
+       }
+
+       @Test
+       public void testErr2() {
+               Result<String> t = Result.err(1, "Error Body");
+               assertNotNull(t);
+               assertThat(t.error, is("Error Body"));
+               assertFalse(t.isOK());
+               assertThat(t.toString(), is("Code: 1 = Error Body"));
+       }
+       
+       @Test
+       public void testOk3() {
+               Result<String> t = Result.ok(1, "Ok");
+               assertNotNull(t);
+               assertThat(t.code, is(1));
+               assertTrue(t.isOK());
+               assertThat(t.toString(), is("Code: 1"));
+       }
+
+       @Test
+       public void testErr3() {
+               Result<String> t = Result.err(1, "Error Body");
+               assertNotNull(t);
+               assertThat(t.error, is("Error Body"));
+               assertFalse(t.isOK());
+               assertThat(t.toString(), is("Code: 1 = Error Body"));
+       }
+}
diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_TestAccess.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_TestAccess.java
new file mode 100644 (file)
index 0000000..7c65fd0
--- /dev/null
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.client.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+
+// TODO: Ian [JUnit] JU_TestAccess isn't implementing all abstract methdods of Access
+public class JU_TestAccess implements Access {
+       private Symm symm;
+
+       public JU_TestAccess() throws CadiException {
+               symm = Symm.obtain(this);
+       }
+       
+       public void log(Level level, Object... elements) {
+               boolean first = true;
+               for(int i=0;i<elements.length;++i) {
+                       if(first)first = false;
+                       else System.out.print(' ');
+                       System.out.print(elements[i].toString());
+               }
+               System.out.println();
+       }
+
+       public void log(Exception e, Object... elements) {
+               e.printStackTrace();
+               log(Level.ERROR,elements);
+       }
+
+       @Override
+       public void printf(Level level, String fmt, Object... elements) {
+               if(willLog(level)) {
+                       System.out.printf(fmt, elements);
+               }
+       }
+
+       public void setLogLevel(Level level) {
+               
+       }
+
+       public ClassLoader classLoader() {
+               return ClassLoader.getSystemClassLoader();
+       }
+
+       public String getProperty(String string, String def) {
+               String rv = System.getProperty(string);
+               return rv==null?def:rv;
+       }
+
+       public void load(InputStream is) throws IOException {
+               
+       }
+
+       public String decrypt(String encrypted, boolean anytext) throws IOException {
+               return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC)))
+                       ? symm.depass(encrypted)
+                       : encrypted;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Access#willLog(org.onap.aaf.cadi.Access.Level)
+        */
+       @Override
+       public boolean willLog(Level level) {
+               return true;
+       }
+
+       @Override
+       public Properties getProperties() {
+               return System.getProperties();
+       }
+
+}
diff --git a/cadi/core/.gitignore b/cadi/core/.gitignore
new file mode 100644 (file)
index 0000000..54c1f5f
--- /dev/null
@@ -0,0 +1,5 @@
+/.settings/
+/war/
+/.classpath
+/.DS_Store
+/target/
diff --git a/cadi/core/.project b/cadi/core/.project
new file mode 100644 (file)
index 0000000..f661737
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>cadi_core</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/cadi/core/conf/.gitignore b/cadi/core/conf/.gitignore
new file mode 100644 (file)
index 0000000..06efae8
--- /dev/null
@@ -0,0 +1 @@
+/keyfile
diff --git a/cadi/core/conf/cadi.properties b/cadi/core/conf/cadi.properties
new file mode 100644 (file)
index 0000000..09c7925
--- /dev/null
@@ -0,0 +1,34 @@
+# 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..1e8ea1c
--- /dev/null
@@ -0,0 +1,95 @@
+<!-- * ============LICENSE_START==================================================== 
+       * org.onap.aaf * =========================================================================== 
+       * 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==================================================== 
+       * -->
+<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.aaf.cadi</groupId>
+               <artifactId>parent</artifactId>
+               <relativePath>..</relativePath>
+               <version>1.5.0-SNAPSHOT</version>
+       </parent>
+
+       <modelVersion>4.0.0</modelVersion>
+       <name>AAF CADI Core Framework</name>
+       <artifactId>aaf-cadi-core</artifactId>
+       <packaging>jar</packaging>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+       <dependencies>
+               <dependency>
+                       <groupId>javax.servlet</groupId>
+                       <artifactId>javax.servlet-api</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+       <build>
+               <plugins>
+                       <plugin>
+                               <!-- Must put this in to turn on Signing, but Configuration itself is 
+                                       in Parent -->
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jarsigner-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                               <configuration>
+                                       <archive>
+                                               <manifest>
+                                                       <mainClass>org.onap.aaf.cadi.CmdLine</mainClass>
+                                               </manifest>
+                                               <manifestEntries>
+                                                       <Sealed>true</Sealed>
+                                               </manifestEntries>
+                                       </archive>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>test-jar</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>test-jar</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+
+</project>
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/AES.java b/cadi/core/src/main/java/org/onap/aaf/cadi/AES.java
new file mode 100644 (file)
index 0000000..3ef3355
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.Symm.Encryption;
+import org.onap.aaf.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) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException {
+               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/aaf/cadi/AbsUserCache.java b/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java
new file mode 100644 (file)
index 0000000..be1e739
--- /dev/null
@@ -0,0 +1,463 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.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<String,Miss>();
+       private final Symm missEncrypt;
+       
+       private Clean clean;
+       protected Access access;
+//     private final static Permission teaser = new LocalPermission("***NoPERM****");
+       
+       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<String, User<PERM>>();
+
+               
+               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));
+                       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<DumpInfo>();
+               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<String, Set<String>>();
+
+       /**
+        * 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<String>(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<User<PERM>>(userMap.values().size());
+                               al.addAll(0, userMap.values());
+                               long now = System.currentTimeMillis() + advance;
+                               for(User<PERM> user : al) {
+                                       ++total;
+                                               if(user.count>usageTriggerCount) {
+       //                                              access.log(Level.AUDIT, "Checking Thread", new Date(now));
+                                                       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();
+                       //                                                              access.log(Level.AUDIT, "CACHE revalidated credentials");
+                                                                                       touched = true;
+                                                                                       break;
+                                                                               default:
+                                                                                       user.resetCount();
+                                                                                       remove(user);
+                                                                                       ++count;
+                                                                                       removed = true;
+                                                                                       break;
+                                                                       }
+                                                               }
+                                                       }
+                                               
+       //                                              access.log(Level.AUDIT, "User Perm Expires", new Date(user.permExpires));
+                                                       if(!removed && lur!=null && user.permExpires<= now ) {
+       //                                                      access.log(Level.AUDIT, "Reloading");
+                                                               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<String>(missTotal);
+                                       keys.addAll(missMap.keySet());
+                                       for(String key : keys) {
+                                               Miss m = missMap.get(key);
+                                               if(m!=null && m.timestamp<System.currentTimeMillis()) {
+                                                       synchronized(missMap) {
+                                                               missMap.remove(key);
+                                                       }
+                                                       access.log(Level.INFO, key, "has been removed from Missed Credential Map (" + m.tries + " invalid tries)");
+                                                       ++miss;
+                                               }
+                                       }
+                               }
+                               
+                               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;
+               
+               public Miss(byte[] first, long timeInterval) {
+                       timestamp = System.currentTimeMillis() + timeInterval;
+                       this.timetolive = timeInterval;
+                       tries = 0L;
+               }
+               
+               
+               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/aaf/cadi/Access.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Access.java
new file mode 100644 (file)
index 0000000..7a9e63b
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.  
+ *  
+ * @author Jonathan
+ *
+ */
+public interface Access {
+       // levels to use
+       public enum Level {
+               DEBUG(0x1), INFO(0x10), AUDIT(0x100), WARN(0x2000), ERROR(0x4000), INIT(0x8000),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/aaf/cadi/BasicCred.java b/cadi/core/src/main/java/org/onap/aaf/cadi/BasicCred.java
new file mode 100644 (file)
index 0000000..b80cda8
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.
+ * 
+ * @author Jonathan
+ *
+ */
+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/aaf/cadi/BufferedServletInputStream.java b/cadi/core/src/main/java/org/onap/aaf/cadi/BufferedServletInputStream.java
new file mode 100644 (file)
index 0000000..2df01cd
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.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.
+ *
+ * 
+ * @author Jonathan
+ *
+ */
+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;
+       }
+
+       public int read(byte[] b) throws IOException {
+               return read(b,0,b.length);
+       }
+
+
+       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;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java
new file mode 100644 (file)
index 0000000..2bb3db3
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.security.Principal;
+
+/**
+ * Cached Principals need to be able to revalidate in the background.
+ * 
+ * @author Jonathan
+ *
+ */
+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/aaf/cadi/CachingLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CachingLur.java
new file mode 100644 (file)
index 0000000..e083f4e
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.security.Principal;
+
+import org.onap.aaf.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/aaf/cadi/CadiException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiException.java
new file mode 100644 (file)
index 0000000..0f250b3
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+/**
+ * CADI Specific Exception
+ * @author Jonathan
+ */
+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/aaf/cadi/CadiWrap.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiWrap.java
new file mode 100644 (file)
index 0000000..49572f4
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.filter.NullPermConverter;
+import org.onap.aaf.cadi.filter.PermConverter;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.taf.TafResp;
+
+
+
+/**
+ * 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,"(HttpRequest)",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 { 
+                       perm = pconv.convert(perm);
+                       if(lur.fish(principal,lur.createPerm(perm))) {
+                               access.log(Level.DEBUG,caller, principal.getName(), "has", perm);
+                               return true;
+                       } else {
+                               access.log(Level.DEBUG,caller, principal.getName(), "does not have", perm);
+                               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<Permission>();
+               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/aaf/cadi/Capacitor.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Capacitor.java
new file mode 100644 (file)
index 0000000..0038385
--- /dev/null
@@ -0,0 +1,241 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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<ByteBuffer>();
+       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/aaf/cadi/CmdLine.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CmdLine.java
new file mode 100644 (file)
index 0000000..90f1440
--- /dev/null
@@ -0,0 +1,361 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.NoSuchAlgorithmException;
+
+import org.onap.aaf.cadi.util.Chmod;
+import org.onap.aaf.cadi.util.JsonOutputStream;
+
+
+
+/**
+ * A Class to run on command line to determine suitability of environment for certain TAFs.
+ * 
+ * For instance, CSP supports services only in certain domains, and while dynamic host
+ * lookups on the machine work in most cases, sometimes, names and IPs are unexpected (and
+ * invalid) for CSP because of multiple NetworkInterfaces, etc
+ * 
+ * @author Jonathan
+ *
+ */
+public class CmdLine {
+
+       public static Access access;
+       
+       /**
+        * @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]) && 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
+                                                                                       && (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 regurgitate 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 StdIin");
+                       System.out.println("                                          if passwd is blank, will ask securely)");
+                       System.out.println("  passgen <digits>                       (Generate Password of given size)");
+                       System.out.println("  urlgen <digits>                        (Generate URL field of given size)");
+                       System.out.println("  csptest                                (Tests for CSP compatibility)");
+                       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)");
+               }
+               String forceExit = access.getProperty("force_exit", null);
+               if (forceExit == null) {
+                       System.exit(1);
+               }
+       }
+       
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java
new file mode 100644 (file)
index 0000000..7f47ce7
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+public interface Connector {
+       public Lur newLur() throws CadiException;
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java
new file mode 100644 (file)
index 0000000..4e42a5f
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+
+/**
+ * UserPass
+ * 
+ * The essential interface required by BasicAuth to determine if a given User/Password combination is 
+ * valid.  This is done as an interface.
+ * 
+ * @author Jonathan
+ */
+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/aaf/cadi/GetCred.java b/cadi/core/src/main/java/org/onap/aaf/cadi/GetCred.java
new file mode 100644 (file)
index 0000000..e64f0dd
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+public interface GetCred {
+       byte[] getCred();
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java
new file mode 100644 (file)
index 0000000..6babb4c
--- /dev/null
@@ -0,0 +1,258 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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;
+       }
+
+       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();
+       }
+
+       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)  throws CadiException{
+               if(!s.startsWith("0x")) {
+                       throw new CadiException("HexString must start with \"0x\"");
+               }
+               boolean high = true;
+               int c;
+               byte b;
+               byte[] ba = new byte[(s.length()-2)/2];
+               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 {
+                               throw new CadiException("Invalid char '" + c + "' in HexString");
+                       }
+                       idx = (i-2)/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) {
+               int c;
+               byte b;
+               byte[] ba;
+               boolean high;
+               int start;
+               if(s.length()%2==0) {
+                       ba = new byte[s.length()/2];
+                       high=true;
+                       start=0;
+               } else {
+                       ba = new byte[(s.length()/2)+1];
+                       high = false;
+                       start=1;
+               }
+               int idx;
+               for(int i=start;i<s.length();++i) {
+                       c = s.charAt((i-start));
+                       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;
+                       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/aaf/cadi/Locator.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Locator.java
new file mode 100644 (file)
index 0000000..22258d1
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/LocatorException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/LocatorException.java
new file mode 100644 (file)
index 0000000..f14fba7
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/Lur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Lur.java
new file mode 100644 (file)
index 0000000..fd73d00
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 
+ * 
+ * @author Jonathan
+ *
+ */
+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/aaf/cadi/Permission.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Permission.java
new file mode 100644 (file)
index 0000000..f806129
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/PropAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/PropAccess.java
new file mode 100644 (file)
index 0000000..07e9b59
--- /dev/null
@@ -0,0 +1,387 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfo;
+
+import java.util.Properties;
+
+public class PropAccess implements Access {
+       private static final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+       public static Level DEFAULT = Level.AUDIT;
+       
+       private Symm symm;
+       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);
+               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 PropAccess(LogIt logit, String[] args) {
+               logIt = logit;
+       }
+       
+       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(Properties p) {
+               // Make sure these two are set before any changes in Logging
+               name = "cadi";
+               level=DEFAULT.maskOf();
+               
+               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);
+               }
+               
+               // Third, load any Chained Property Files
+               load(props.getProperty(Config.CADI_PROP_FILES));
+               
+               String sLevel = props.getProperty(Config.CADI_LOGLEVEL); 
+               if(sLevel!=null) {
+                       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);
+               
+               specialConversions();
+       }
+
+       private void specialConversions() {
+               // Critical - if no Security Protocols set, then set it.  We'll just get messed up if not
+               if(props.get(Config.CADI_PROTOCOLS)==null) {
+                       props.setProperty(Config.CADI_PROTOCOLS, SecurityInfo.HTTPS_PROTOCOLS_DEFAULT);
+               }
+               
+               Object temp;
+               temp=props.get(Config.CADI_PROTOCOLS);
+               if(props.get(Config.HTTPS_PROTOCOLS)==null && temp!=null) {
+                       props.put(Config.HTTPS_PROTOCOLS, temp);
+               }
+               
+               if(temp!=null) {
+                       if("1.7".equals(System.getProperty("java.specification.version")) 
+                                       && (temp==null || (temp instanceof String && ((String)temp).contains("TLSv1.2")))) {
+                               System.setProperty(Config.HTTPS_CIPHER_SUITES, Config.HTTPS_CIPHER_SUITES_DEFAULT);
+                       }
+               }
+       }
+
+       private void load(String cadi_prop_files) {
+               if(cadi_prop_files==null) {
+                       return;
+               }
+               String prevKeyFile = props.getProperty(Config.CADI_KEYFILE);
+               int prev = 0, end = cadi_prop_files.length();
+               int idx;
+               String filename;
+               while(prev<end) {
+                       idx = cadi_prop_files.indexOf(File.pathSeparatorChar,prev);
+                       if(idx<0) {
+                               idx = end;
+                       }
+                       File file = new File(filename=cadi_prop_files.substring(prev,idx));
+                       if(file.exists()) {
+                               printf(Level.INIT,"Loading CADI Properties from %s",file.getAbsolutePath());
+                               try {
+                                       FileInputStream fis = new FileInputStream(file);
+                                       try {
+                                               props.load(fis);
+                                               // Recursively Load
+                                               String chainProp = props.getProperty(Config.CADI_PROP_FILES);
+                                               if(chainProp!=null) {
+                                                       if(recursionProtection==null) {
+                                                               recursionProtection = new ArrayList<String>();
+                                                               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());
+                       }
+                       prev = idx+1;
+               }
+               
+               // Trim 
+               for(Entry<Object, Object> es : props.entrySet()) {
+                       Object value = es.getValue();
+                       if(value instanceof String) {
+                               String trim = ((String)value).trim();
+                               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);
+                       }
+               }
+               
+               specialConversions();
+       }
+       
+       @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);
+               }
+       }
+       
+       protected StringBuilder buildMsg(Level level, Object[] elements) {
+               StringBuilder sb = new StringBuilder(iso8601.format(new Date()));
+               sb.append(' ');
+               sb.append(level.name());
+               sb.append(" [");
+               sb.append(name);
+               
+               int end = elements.length;
+               if(end<=0) {
+                       sb.append("] ");
+               } else {
+                       int idx = 0;
+                       if(elements[idx] instanceof Integer) {
+                               sb.append('-');
+                               sb.append(elements[idx]);
+                               ++idx;
+                       }
+                       sb.append("] ");
+                       String s;
+                       boolean first = true;
+                       for(Object o : elements) {
+                               if(o!=null) {
+                                       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 sb;
+       }
+
+       @Override
+       public void log(Exception e, Object... elements) {
+               log(Level.ERROR,e.getMessage(),elements);
+               e.printStackTrace(System.err);
+       }
+
+       @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;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java
new file mode 100644 (file)
index 0000000..125ac24
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+
+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/aaf/cadi/SecuritySetter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/SecuritySetter.java
new file mode 100644 (file)
index 0000000..3156301
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+
+/**
+ *  Apply any particular security mechanism
+ *  
+ *  This allows the definition of various mechanisms involved outside of DRcli jars 
+ *  
+ * @author Jonathan
+ *
+ */
+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/aaf/cadi/ServletContextAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/ServletContextAccess.java
new file mode 100644 (file)
index 0000000..38a01a0
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import java.util.Enumeration;
+
+import javax.servlet.FilterConfig;
+import javax.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.aaf.cadi.PropAccess#log(org.onap.aaf.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.aaf.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/aaf/cadi/Symm.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Symm.java
new file mode 100644 (file)
index 0000000..095f6e9
--- /dev/null
@@ -0,0 +1,854 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.Random;
+
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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. 
+ * 
+ * However, this algorithm has not been tested against hackers.  Until such a time, utilize more tested
+ * packages to protect Data, especially sensitive data at rest (long term). 
+ * 
+ * @author Jonathan
+ *
+ */
+public class Symm {
+       private static final byte[] DOUBLE_EQ = new byte[] {'=','='}; 
+       public static final String ENC = "enc:";
+       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
+       
+       /**
+        * This is the standard base64 Key Set.
+        * RFC 2045
+        */
+       public static final Symm base64 = new Symm(
+                       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+                       ,76, Config.UTF_8,true);
+
+       public static final Symm base64noSplit = new Symm(
+                       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+                       ,Integer.MAX_VALUE, Config.UTF_8,true);
+
+       /**
+        * 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);
+
+       /**
+        * A Password set, using US-ASCII
+        * RFC 4648
+        */
+       public static final Symm encrypt = new Symm(base64url.codeset,1024, "US-ASCII", false);
+       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();
+                       
+
+
+       /**
+        * 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) {
+               this.codeset = codeset;
+               splitLinesAt = split;
+               encoding = charset;
+               endEquals = useEndEquals;
+               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<int[]>();
+               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);
+       }
+       
+       // 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(ENC) {
+                       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 {
+       is.skip(skip);
+       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 {
+                  switch(read) {
+                          case -1: 
+                          case '=':
+                          case '\n': 
+                                  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': 
+                                  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 {
+               Symm symm = Symm.baseCrypt();
+
+               String keyfile = access.getProperty(Config.CADI_KEYFILE,null);
+               if(keyfile!=null) {
+                       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;
+   }
+  /**
+   *  Create a new random key 
+   */
+  public Symm obtain() throws IOException {
+               byte inkey[] = new byte[0x800];
+               new SecureRandom().nextBytes(inkey);
+               return obtain(inkey);
+  }
+  
+  /**
+   * Obtain a Symm from 2048 key from a String
+   * 
+   * @param key
+   * @return
+   * @throws IOException
+   */
+  public static Symm obtain(String key) throws IOException {
+         return obtain(new ByteArrayInputStream(key.getBytes()));
+  }
+  
+  /**
+   * 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");
+         }
+         return baseCrypt().obtain(bkey);
+  }
+
+  /**
+   * Convenience for picking up Keyfile
+   * 
+   * @param f
+   * @return
+   * @throws IOException
+   */
+  public static Symm obtain(File f) throws IOException {
+         FileInputStream fis = new FileInputStream(f);
+         try {
+                 return obtain(fis);
+         } 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 {
+               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);
+         // 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;
+  }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java
new file mode 100644 (file)
index 0000000..1767258
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import org.onap.aaf.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/aaf/cadi/Transmutate.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Transmutate.java
new file mode 100644 (file)
index 0000000..6372225
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/TrustChecker.java b/cadi/core/src/main/java/org/onap/aaf/cadi/TrustChecker.java
new file mode 100644 (file)
index 0000000..fabec0b
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.onap.aaf.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/aaf/cadi/User.java b/cadi/core/src/main/java/org/onap/aaf/cadi/User.java
new file mode 100644 (file)
index 0000000..5e9f8a5
--- /dev/null
@@ -0,0 +1,177 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.lur.LocalPermission;
+
+/**
+ * Class to hold info from the User Perspective.
+ * 
+ * @author Jonathan
+ *
+ */
+public final class User<PERM extends Permission> {
+       private static final Map<String,Permission> NULL_MAP = new HashMap<String,Permission>();
+       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<String,Permission>();
+       }
+
+       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/aaf/cadi/UserChain.java b/cadi/core/src/main/java/org/onap/aaf/cadi/UserChain.java
new file mode 100644 (file)
index 0000000..e423b8b
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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@csp.att.com)
+ *  Protocol is the Security protocol,
+ *  
+ *  Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]*
+ *  
+ * 
+ * @author Jonathan
+ *
+ */
+public interface UserChain  {
+       public enum Protocol {BasicAuth,Cookie,Cert,OAuth};
+       public String userChain();
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java
new file mode 100644 (file)
index 0000000..dda4b6c
--- /dev/null
@@ -0,0 +1,728 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.List;
+
+import org.onap.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachingLur;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Connector;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.TrustChecker;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.lur.LocalLur;
+import org.onap.aaf.cadi.lur.NullLur;
+import org.onap.aaf.cadi.taf.HttpEpiTaf;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.basic.BasicHttpTaf;
+import org.onap.aaf.cadi.taf.cert.X509Taf;
+import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf;
+
+/**
+ * Create a Consistent Configuration mechanism, even when configuration styles are as vastly different as
+ * Properties vs JavaBeans vs FilterConfigs...
+ * 
+ * @author Jonathan
+ *
+ */
+public class Config {
+
+       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_LOGNAME = "cadi_logname";
+       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_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";
+       
+       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 CSP_DOMAIN = "csp_domain";
+       public static final String CSP_HOSTNAME = "csp_hostname";
+       public static final String CSP_DEVL_LOCALHOST = "csp_devl_localhost";
+       public static final String CSP_USER_HEADER = "CSP_USER";
+       public static final String CSP_SYSTEMS_CONF = "CSPSystems.conf";
+    public static final String CSP_SYSTEMS_CONF_FILE = "csp_systems_conf_file";
+    
+    public static final String HTTPS_PROTOCOLS = "https.protocols";
+    public static final String HTTPS_CIPHER_SUITES = "https.cipherSuites";
+    public static final String HTTPS_CLIENT_PROTOCOLS="jdk.tls.client.protocols";
+    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_URL = "aaf_url"; //URL for AAF... Use to trigger AAF configuration
+       public static final String AAF_ROOT_NS = "aaf_root_ns";
+       public static final String AAF_ROOT_COMPANY = "aaf_root_company";
+       public static final String AAF_LOCATE_URL = "aaf_locate_url"; //URL for AAF locator
+       private static final String AAF_LOCATE_URL_TAG = "AAF_LOCATE_URL"; // Name of Above for use in Config Variables.
+       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_TAF_CLASS_DEF = "org.osaaf.cadi.aaf.v2_0.AAFTaf";
+       public static final String AAF_CONNECTOR_CLASS = "aaf_connector_class";
+       public static final String AAF_LOCATOR_CLASS = "aaf_locator_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.
+       
+       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_DEFAULT_VERSION = "2.0";
+       public static final String AAF_DATA_DIR = "aaf_data_dir"; // AAF processes and Components only.
+
+
+       
+       public static final String GW_URL = "gw_url";
+       public static final String CM_URL = "cm_url";
+       public static final String CM_TRUSTED_CAS = "cm_trusted_cas";
+
+       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="none";
+
+       public static final String AAF_DOMAIN_SUPPORT = "aaf_domain_support";
+       public static final String AAF_DOMAIN_SUPPORT_DEF = ".com";
+
+       // 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) throws CadiException {
+               try {
+                       boolean hasCSP;
+                       try {
+                               Class.forName("org.osaaf.cadi.taf.csp.CSPTaf");
+                               hasCSP=true;
+                       } catch(ClassNotFoundException e) {
+                               hasCSP = logProp(access,Config.CSP_DOMAIN, null)!=null;
+                       }
+                       defaultRealm = logProp(access,Config.AAF_DEFAULT_REALM,
+                                       hasCSP?"csp.att.com":
+                                       logProp(access,Config.BASIC_REALM,
+                                               logProp(access,HOSTNAME,InetAddress.getLocalHost().getHostName())
+                                               )
+                                       );
+               } catch (UnknownHostException e) {
+                       //defaultRealm="none";
+               }
+       }
+
+       public static HttpTaf configHttpTaf(Connector con, SecurityInfoC<HttpURLConnection> si, TrustChecker tc, CredVal up, Lur lur, Object ... additionalTafLurs) throws CadiException {
+               Access access = si.access;
+               /////////////////////////////////////////////////////
+               // Setup AAFCon for any following
+               /////////////////////////////////////////////////////
+               Class<?> aafConClass = loadClass(access,"org.onap.aaf.cadi.aaf.v2_0.AAFCon");
+               Object aafcon = null;
+               if(con!=null && aafConClass!=null && aafConClass.isAssignableFrom(con.getClass())) {
+                       aafcon = con;
+               } else if(lur != null) {
+                       Field f = null;
+                       try {
+                               f = lur.getClass().getField("aaf");
+                               aafcon = f.get(lur);
+                       } catch (Exception nsfe) {
+                       }
+               }
+       
+               
+               boolean hasDirectAAF = hasDirect("DirectAAFLur",additionalTafLurs);
+               // IMPORTANT!  Don't attempt to load AAF Connector if there is no AAF URL
+               String aafURL = access.getProperty(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<HttpTaf> htlist = new ArrayList<HttpTaf>();
+
+               /////////////////////////////////////////////////////
+               // 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 DenialOfServiceTaf(access));
+
+               /////////////////////////////////////////////////////
+               // Configure Client Cert TAF
+               /////////////////////////////////////////////////////
+               
+               String truststore = logProp(access, CADI_TRUSTSTORE,null);
+               if(truststore!=null) {
+                       String truststore_pwd = access.getProperty(CADI_TRUSTSTORE_PASSWORD,null);
+                       if(truststore_pwd!=null) {
+                               if(truststore_pwd.startsWith(Symm.ENC)) {
+                                       try {
+                                               truststore_pwd = access.decrypt(truststore_pwd,false);
+                                       } catch (IOException e) {
+                                               throw new CadiException(CADI_TRUSTSTORE_PASSWORD + " cannot be decrypted",e);
+                                       }
+                               }
+                               try {
+                                       htlist.add(new X509Taf(access,lur));
+                                       access.log(Level.INIT,"Certificate Authorization enabled");
+                               } catch (SecurityException e) {
+                                       access.log(Level.INIT,"AAFListedCertIdentity cannot be instantiated. Certificate Authorization is now disabled",e);
+                               } catch (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 basic_realm = 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 basic_warn = "TRUE".equals(access.getProperty(BASIC_WARN,"FALSE"));
+
+               if(!hasDirectAAF) {
+                       HttpTaf aaftaf=null;
+                       if(!hasOAuthDirectTAF) {
+                               if(basic_realm!=null) {
+                                       @SuppressWarnings("unchecked")
+                                       Class<HttpTaf> obasicCls = (Class<HttpTaf>)loadClass(access,"org.osaaf.cadi.obasic.OBasicHttpTaf");
+                                       if(obasicCls!=null) {
+                                               try {
+                                                       String tokenurl = logProp(access,Config.AAF_OAUTH2_TOKEN_URL, null);
+                                                       String introspecturl = logProp(access,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.");
+                                                       }
+                                                       Constructor<HttpTaf> obasicConst = obasicCls.getConstructor(PropAccess.class,String.class, String.class, String.class);
+                                                       htlist.add(obasicConst.newInstance(access,basic_realm,tokenurl,introspecturl));
+                                                       access.log(Level.INIT,"Oauth supported Basic Authorization is enabled");
+                                               } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                                               }
+                                       } else if(up!=null) {
+                                               access.log(Level.INIT,"Basic Authorization is enabled using realm",basic_realm);
+                                               // Allow warning about insecure channel to be turned off
+                                               if(!basic_warn)access.log(Level.INIT,"WARNING! The basic_warn property has been set to false.",
+                                                               " There will be no additional warning if Basic Auth is used on an insecure channel"
+                                                               );
+                                               htlist.add(new BasicHttpTaf(access, up, basic_realm, userExp, basic_warn));
+                                               access.log(Level.INIT,"Basic Authorization is enabled");
+                                       }
+                               } else {
+                                       access.log(Level.INIT,"Local Basic Authorization is disabled.  Enable by setting basic_realm=<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);
+               
+                                                       Constructor<?> cstr = aafTafClass.getConstructor(Connector.class,boolean.class,AbsUserCache.class);
+                                                       if(cstr!=null) {
+                                                               if(lur instanceof AbsUserCache) {
+                                                                       aaftaf = (HttpTaf)cstr.newInstance(aafcon,basic_warn,lur);
+                                                               } else {
+                                                                       cstr = aafTafClass.getConstructor(Connector.class,boolean.class);
+                                                                       if(cstr!=null) {
+                                                                               aaftaf = (HttpTaf)cstr.newInstance(aafcon,basic_warn);
+                                                                       }
+                                                               }
+                                                               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
+                                                               }
+                                                       }
+                                               } catch(Exception e) {
+                                                       access.log(Level.INIT,"ERROR! AAF TAF Failed construction.  NOT Configured",e);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       /////////////////////////////////////////////////////
+                       // Configure OAuth TAF
+                       /////////////////////////////////////////////////////
+                       if(!hasOAuthDirectTAF) {
+                               String oauth_token_url = logProp(access,Config.AAF_OAUTH2_TOKEN_URL,null);
+                               if(additionalTafLurs!=null && additionalTafLurs.length>0 && additionalTafLurs[0].getClass().getName().equals("org.osaaf.authz.oauth.OAuthDirectTAF")) {
+                                       htlist.add((HttpTaf)additionalTafLurs[0]);
+                                       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(oauth_token_url!=null) {
+                                       String oauth_introspect_url = logProp(access,Config.AAF_OAUTH2_INTROSPECT_URL,null);
+                                       @SuppressWarnings("unchecked")
+                                       Class<HttpTaf> oaTCls = (Class<HttpTaf>)loadClass(access,"org.osaaf.cadi.oauth.OAuth2HttpTaf");
+                                       if(oaTCls!=null) {
+                                               Class<?> oaTTmgrCls = loadClass(access, "org.osaaf.cadi.oauth.TokenMgr");
+                                               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,oauth_token_url,oauth_introspect_url);
+                                                               Constructor<HttpTaf> oaTConst = oaTCls.getConstructor(Access.class,oaTTmgrCls);
+                                                               htlist.add(oaTConst.newInstance(access,oaTTmgr));
+                                                               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(aaftaf);
+                       }
+               }       
+
+               /////////////////////////////////////////////////////
+               // Any Additional Lurs passed in Constructor
+               /////////////////////////////////////////////////////
+               if(additionalTafLurs!=null) {
+                       for(Object additional : additionalTafLurs) {
+                               if(additional instanceof HttpTaf) {
+                                       htlist.add((HttpTaf)additional);
+                                       access.printf(Level.INIT,"%s Authentication is enabled",additional.getClass().getSimpleName());
+                               } else if(hasOAuthDirectTAF && additional.getClass().getSimpleName().equals("DirectAAFUserPass")) {
+                                       htlist.add(new BasicHttpTaf(access, (CredVal)additional , basic_realm, userExp, basic_warn));
+                                       access.printf(Level.INIT,"Direct BasicAuth Authentication is enabled",additional.getClass().getSimpleName());
+                               }
+                       }
+               }
+               
+               /////////////////////////////////////////////////////
+               // Create EpiTaf from configured TAFs
+               /////////////////////////////////////////////////////
+               if(htlist.size()==1) {
+                       // just return the one
+                       taf = htlist.get(0);
+               } else {
+                       HttpTaf[] htarray = new HttpTaf[htlist.size()];
+                       htlist.toArray(htarray);
+                       Locator<URI> locator = loadLocator(si, logProp(access, AAF_LOCATE_URL, null));
+                       
+                       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(Access access,String tag, String def) {
+               String rv = access.getProperty(tag, def);
+               if(rv == null) {
+                       access.log(Level.INIT,tag,"is not 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;
+               List<Lur> lurs = new ArrayList<Lur>();
+               
+               /////////////////////////////////////////////////////
+               // 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;
+                               lurs.add(ll = new LocalLur(access, users, groups)); // note b64==null is ok.. just means no encryption.
+                               
+                               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 token_url = logProp(access,AAF_OAUTH2_TOKEN_URL, null);
+               String introspect_url = logProp(access,AAF_OAUTH2_INTROSPECT_URL, null);
+               if(token_url!=null && introspect_url !=null) {
+                       try {
+                               Class<?> olurCls = loadClass(access, "org.osaaf.cadi.olur.OLur");
+                               if(olurCls!=null) {
+                                       Constructor<?> olurCnst = olurCls.getConstructor(PropAccess.class,String.class,String.class);
+                                       Lur olur = (Lur)olurCnst.newInstance(access,token_url,introspect_url);
+                                       lurs.add(olur);
+                                       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) {
+                               access.log(e,"AAF/OAuth LUR could not be constructed with given Constructors.");
+                       } 
+               } else {
+                       access.log(Level.INIT, "OAuth2 Lur disabled");
+               }
+
+               if(con!=null) { // try to reutilize connector
+                       lurs.add(con.newLur());
+               } else { 
+                       /////////////////////////////////////////////////////
+                       // Configure the AAF Lur (if any)
+                       /////////////////////////////////////////////////////
+                       String aafURL = logProp(access,AAF_URL,null); // Trigger Property
+                       String aaf_env = access.getProperty(AAF_ENV,null);
+                       if(aaf_env == 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) {
+                                               aaf_env = aafURL.substring(ec, slash);
+                                               ((PropAccess)access).setProperty(AAF_ENV, aaf_env);
+                                               access.printf(Level.INIT, "Setting aaf_env to %s from aaf_url value",aaf_env);
+                                       }
+                               }
+                       }
+
+                       // 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,"org.osaaf.cadi.aaf.v2_0.AAFLurPerm");
+                                       ////////////AAF Lur 2.0 /////////////
+                                       if(aafLurClassStr.startsWith("org.osaaf.cadi.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, "org.osaaf.cadi.aaf.v2_0.AAFCon");
+                                                               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((Lur)aaflur);
+                                                                       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) {
+                       for(Object additional : additionalTafLurs) {
+                               if(additional instanceof Lur) {
+                                       lurs.add((Lur)additional);
+                                       access.log(Level.INIT, additional);
+                               }
+                       }
+               }
+
+               /////////////////////////////////////////////////////
+               // 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); // Only one, just return it, save processing
+                       default:
+                               // Multiple Lurs, use EpiLUR to handle
+                               Lur[] la = new Lur[lurs.size()];
+                               lurs.toArray(la);
+                               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;
+       }
+
+       private static final String COM_ATT_CADI_AAF_V2_0_AAF_CON_HTTP = "org.osaaf.cadi.aaf.v2_0.AAFConHttp";
+       public static Object loadAAFConnector(SecurityInfoC<HttpURLConnection> si, String aafURL) {
+               Access access = si.access;
+               Object aafcon = null;
+               Class<?> aafConClass = null;
+
+               try {
+                       if(aafURL!=null) {
+                               String aafConnector = access.getProperty(AAF_CONNECTOR_CLASS, COM_ATT_CADI_AAF_V2_0_AAF_CON_HTTP);
+                       if(COM_ATT_CADI_AAF_V2_0_AAF_CON_HTTP.equals(aafConnector)) {
+                                       aafConClass = loadClass(access, COM_ATT_CADI_AAF_V2_0_AAF_CON_HTTP);
+                                       for(Constructor<?> c : aafConClass.getConstructors()) {
+                                               List<Object> lo = new ArrayList<Object>();
+                                               for(Class<?> pc : c.getParameterTypes()) {
+                                                       if(pc.equals(PropAccess.class)) {
+                                                               lo.add(access);
+                                                       } else if(pc.equals(Locator.class)) {
+                                                               lo.add(loadLocator(si, aafURL));
+                                                       } else {
+                                                               continue;
+                                                       }
+                                               }
+                                               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) {
+                                                       // 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) {
+                       try {
+                               cls = access.getClass().getClassLoader().loadClass(className);
+                       } catch (ClassNotFoundException cnfe2) {
+                               // just return null
+                       }
+               }
+               return cls;
+       }
+
+
+       @SuppressWarnings("unchecked")
+       public static Locator<URI> loadLocator(SecurityInfoC<HttpURLConnection> si, final String _url) {
+               Access access = si.access;
+               String url = _url, replacement;
+               int idxAAF_LOCATE_URL;
+               if((idxAAF_LOCATE_URL=_url.indexOf(AAF_LOCATE_URL_TAG))>0 && ((replacement=access.getProperty(AAF_LOCATE_URL, null))!=null)) {
+                       url = replacement + "/locate" + _url.substring(idxAAF_LOCATE_URL+AAF_LOCATE_URL_TAG.length());
+               }
+
+               Locator<URI> locator = null;
+               if(url==null) {
+                       access.log(Level.INIT,"No URL passed to 'loadLocator'. Disabled");
+               } else {
+                       try {
+                               Class<?> lcls = loadClass(access,"org.onap.aaf.cadi.aaf.v2_0.AAFLocator");
+                               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",String.class);
+                                       locator = (Locator<URI>)meth.invoke(null,url);
+                               } catch (Exception e) {
+                                       locator = null;
+                               }
+                               if(locator==null) {
+                                       URI locatorURI = new URI(url);
+                                       Constructor<?> cnst = lcls.getConstructor(new Class[] {SecurityInfoC.class,URI.class});
+                                       locator = (Locator<URI>)cnst.newInstance(new Object[] {si,locatorURI});
+                                       int port = locatorURI.getPort();
+                                       String portS = port<0?"":(":"+locatorURI.getPort());
+                                       
+                                       access.log(Level.INFO, "AAFLocator enabled using " + locatorURI.getScheme() +"://"+locatorURI.getHost() + portS);
+                               } else {
+                                       access.log(Level.INFO, "AAFLocator enabled using preloaded " + locator.getClass().getSimpleName());
+                               }
+                       } catch (InvocationTargetException e) {
+                               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;
+       }
+
+}
+
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java
new file mode 100644 (file)
index 0000000..dfb7b4d
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.config;
+
+import java.lang.reflect.Method;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.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/aaf/cadi/config/GetAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/GetAccess.java
new file mode 100644 (file)
index 0000000..05ee90c
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.config;
+
+import org.onap.aaf.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.aaf.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.aaf.cadi.PropAccess#getProperty(java.lang.String)
+        */
+       @Override
+       public String getProperty(String tag) {
+               String rv;
+               rv = super.getProperty(tag, null);
+               if(rv==null && getter!=null) {
+                       rv = getter.get(tag, null, true);
+               }
+               return rv;
+       }
+
+       public Get get() {
+               return getter;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java
new file mode 100644 (file)
index 0000000..a73df14
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/config/SecurityInfo.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfo.java
new file mode 100644 (file)
index 0000000..99f4136
--- /dev/null
@@ -0,0 +1,255 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.util.MaskFormatException;
+import org.onap.aaf.cadi.util.NetMask;
+
+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";
+
+       public static final String HTTPS_PROTOCOLS_DEFAULT = "TLSv1.1,TLSv1.2";
+       public static final String REGEX_COMMA = "\\s*,\\s*";
+       public static final String SslKeyManagerFactoryAlgorithm;
+       
+       private SSLSocketFactory scf;
+       private X509KeyManager[] km;
+       private X509TrustManager[] tm;
+       public final String default_alias;
+       private NetMask[] trustMasks;
+       private SSLContext ctx;
+       private HostnameVerifier maskHV;
+       public final Access access;
+
+       // Change Key Algorithms for IBM's VM.  Could put in others, if needed.
+       static {
+               if(System.getProperty("java.vm.vendor").equalsIgnoreCase("IBM Corporation")) {
+                       SslKeyManagerFactoryAlgorithm = "IbmX509";
+               } else {
+                       SslKeyManagerFactoryAlgorithm = "SunX509";
+               }
+       }
+       
+
+       public SecurityInfo(final Access access) throws CadiException {
+               try {
+                       this.access = access;
+                       // reuse DME2 Properties for convenience if specific Properties don't exist
+                       String keyStore = access.getProperty(Config.CADI_KEYSTORE,null);
+                       if(keyStore!=null && !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);
+                       String trustStore = access.getProperty(Config.CADI_TRUSTSTORE, null);
+                       if(trustStore!=null && !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);
+                       default_alias = access.getProperty(Config.CADI_ALIAS,null);
+                       
+                       String keyPasswd = access.getProperty(Config.CADI_KEY_PASSWORD,null);
+                       keyPasswd = keyPasswd==null?keyStorePasswd:access.decrypt(keyPasswd,false);
+                       String tips=access.getProperty(Config.CADI_TRUST_MASKS, null);
+                       if(tips!=null) {
+                               access.log(Level.INIT,"Explicitly accepting valid X509s from",tips);
+                               String[] ipsplit = tips.split(REGEX_COMMA);
+                               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);
+                                       }
+                               }
+                       }
+                       String https_protocols = Config.logProp(access,Config.CADI_PROTOCOLS,
+                                               access.getProperty(HTTPS_PROTOCOLS,HTTPS_PROTOCOLS_DEFAULT)
+                                               );
+                       System.setProperty(HTTPS_PROTOCOLS,https_protocols);
+                       System.setProperty(JDK_TLS_CLIENT_PROTOCOLS, https_protocols);
+                       if("1.7".equals(System.getProperty("java.specification.version")) && https_protocols.contains("TLSv1.2")) {
+                               System.setProperty(Config.HTTPS_CIPHER_SUITES, Config.HTTPS_CIPHER_SUITES_DEFAULT);
+                       }
+                       
+                       KeyManagerFactory kmf = KeyManagerFactory.getInstance(SslKeyManagerFactoryAlgorithm);
+                       File file;
+       
+       
+                       if(keyStore==null || keyStorePasswd == null) { 
+                               km = new X509KeyManager[0];
+                       } else {
+                               ArrayList<X509KeyManager> kmal = new ArrayList<X509KeyManager>();
+                               for(String ksname : keyStore.split(REGEX_COMMA)) {
+                                       file = new File(ksname);
+                                       String keystoreFormat;
+                                       if(ksname.endsWith(".p12") || ksname.endsWith(".pkcs12")) {
+                                               keystoreFormat = "PKCS12";
+                                       } else {
+                                               keystoreFormat = "JKS";
+                                       }
+                                       if(file.exists()) {
+                                               FileInputStream fis = new FileInputStream(file);
+                                               try {
+                                                       KeyStore ks = KeyStore.getInstance(keystoreFormat);
+                                                       ks.load(fis, keyStorePasswd.toCharArray());
+                                                       kmf.init(ks, keyPasswd.toCharArray());
+                                               } finally {
+                                                       fis.close();
+                                               }
+                                       }
+                               }
+                               for(KeyManager km : kmf.getKeyManagers()) {
+                                       if(km instanceof X509KeyManager) {
+                                               kmal.add((X509KeyManager)km);
+                                       }
+                               }
+                               km = new X509KeyManager[kmal.size()];
+                               kmal.toArray(km);
+                       }
+       
+                       TrustManagerFactory tmf = TrustManagerFactory.getInstance(SslKeyManagerFactoryAlgorithm);
+                       if(trustStore!=null) {
+                               for(String tsname : trustStore.split(REGEX_COMMA)) {
+                                       file = new File(tsname);
+                                       if(file.exists()) {
+                                               FileInputStream fis = new FileInputStream(file);
+                                               try {
+                                                       KeyStore ts = KeyStore.getInstance("JKS");
+                                                       ts.load(fis, trustStorePasswd.toCharArray());
+                                                       tmf.init(ts); 
+                                               } finally {
+                                                       fis.close();
+                                               }
+                                       }
+                               }
+                               TrustManager tms[] = tmf.getTrustManagers();
+                               tm = new X509TrustManager[tms==null?0:tms.length];
+                               for(int i=0;i<tms.length;++i) {
+                                       try {
+                                               tm[i]=(X509TrustManager)tms[i];
+                                       } catch (ClassCastException e) {
+                                               access.log(Level.WARN, "Non X509 TrustManager", tm[i].getClass().getName(),"skipped in SecurityInfo");
+                                       }
+                               }
+                       }
+                       
+                       if(trustMasks!=null) {
+                               final HostnameVerifier origHV = HttpsURLConnection.getDefaultHostnameVerifier();
+                               HttpsURLConnection.setDefaultHostnameVerifier(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);
+                                       };
+                               });
+                       }
+                       ctx = SSLContext.getInstance("TLS");
+                       ctx.init(km, tm, null);
+                       SSLContext.setDefault(ctx);
+                       scf = ctx.getSocketFactory();
+               } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException e) {
+                       throw new CadiException(e);
+               }
+       }
+
+       /**
+        * @return the scf
+        */
+       public SSLSocketFactory getSSLSocketFactory() {
+               return scf;
+       }
+
+       public SSLContext getSSLContext() {
+               return ctx;
+       }
+
+       /**
+        * @return the km
+        */
+       public X509KeyManager[] getKeyManagers() {
+               return km;
+       }
+
+       public void checkClientTrusted(X509Certificate[] certarr) throws CertificateException {
+               for(X509TrustManager xtm : tm) {
+                       xtm.checkClientTrusted(certarr, SECURITY_ALGO);
+               }
+       }
+
+       public void checkServerTrusted(X509Certificate[] certarr) throws CertificateException {
+               for(X509TrustManager xtm : tm) {
+                       xtm.checkServerTrusted(certarr, SECURITY_ALGO);
+               }
+       }
+
+       public void setSocketFactoryOn(HttpsURLConnection hsuc) {
+               hsuc.setSSLSocketFactory(scf);
+               if(maskHV!=null && !maskHV.equals(hsuc.getHostnameVerifier())) {
+                       hsuc.setHostnameVerifier(maskHV);
+               }
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java
new file mode 100644 (file)
index 0000000..33aef6c
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.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<Class<?>,SecurityInfoC<?>>();
+       public SecuritySetter<CLIENT> defSS;
+
+       private SecurityInfoC(Access access) throws CadiException {
+               super(access);
+               defSS = new SecuritySetter<CLIENT>() {
+                               @Override
+                               public String getID() {
+                                       return DEF_ID;
+                               }
+
+                               @Override
+                               public void setSecurity(CLIENT client) throws CadiException {
+                                       throw new CadiException("No Client Credentials set.");
+                               }
+
+                               @Override
+                               public int setLastResponse(int respCode) {
+                                       return 0;
+                               }
+                       };
+       }
+       
+       @SuppressWarnings("unchecked")
+       public static synchronized <CLIENT> SecurityInfoC<CLIENT> instance(Access access, Class<CLIENT> cls) throws CadiException {
+               SecurityInfoC<?> sic = sicMap.get(cls);
+               if(sic==null) {
+                       sic = new SecurityInfoC<CLIENT>(access); 
+                       sicMap.put(cls, sic);
+               }
+               return (SecurityInfoC<CLIENT>)sic;
+       }
+
+       public SecurityInfoC<CLIENT> set(SecuritySetter<CLIENT> defSS) {
+               this.defSS = defSS;
+               return this;
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java
new file mode 100644 (file)
index 0000000..c03be1f
--- /dev/null
@@ -0,0 +1,157 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.AbsUserCache;
+import org.onap.aaf.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<String>();
+                       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 (Throwable 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()];
+                                               try {
+                                                       fis.read(orig);
+                                               } finally {
+                                                       fis.close();
+                                               }
+                                               // 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/aaf/cadi/filter/AUTHZ.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZ.java
new file mode 100644 (file)
index 0000000..7fd1e93
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 javax.servlet.Servlet;
+
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface AUTHZ {
+       Class<? extends Servlet> value();
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java
new file mode 100644 (file)
index 0000000..f7c4b7f
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.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);
+               } else { // Validate
+                       try {
+                               HttpServletRequest hreq = (HttpServletRequest)req;
+                               boolean proceed = false;
+                               for(String role : roles) {
+                                       if(hreq.isUserInRole(role)) {
+                                               proceed = true;
+                                               break;
+                                       }
+                               }
+                               if(proceed) {
+                                       delegate.service(req,resp);
+                               } else {
+                                       //baseRequest.getServletContext().log(hreq.getUserPrincipal().getName()+" Refused " + roles);
+                                       ((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/aaf/cadi/filter/AccessGetter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AccessGetter.java
new file mode 100644 (file)
index 0000000..ab34a0a
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.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/aaf/cadi/filter/CadiFilter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java
new file mode 100644 (file)
index 0000000..8577d55
--- /dev/null
@@ -0,0 +1,332 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.ServletContextAccess;
+import org.onap.aaf.cadi.TrustChecker;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.Get;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+
+/**
+ * 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 Filter oauthFilter;
+       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")
+       private void init(Get getter) throws ServletException {
+        // 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.aaf.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.aaf.cadi.oauth.OAuthFilter");
+                          oauthFilter = cf.newInstance();
+                  } catch (ClassNotFoundException e) {
+                          oauthFilter = new Filter() { // Null Filter
+                                       @Override
+                                       public void destroy() {
+                                       }
+       
+                                       @Override
+                                       public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throws IOException, ServletException {
+                                               chain.doFilter(req, resp);
+                                       }
+       
+                                       @Override
+                                       public void init(FilterConfig arg0) throws ServletException {
+                                       }
+                          };
+                  }
+          } 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 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<Pair>();
+                                                       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");
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // 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 {
+               try {
+                       HttpServletRequest hreq = (HttpServletRequest)request;
+                       if(noAuthn(hreq)) {
+                               chain.doFilter(request, response);
+                       } else {
+                               HttpServletResponse hresp = (HttpServletResponse)response;
+                               TafResp tresp = httpChecker.validate(hreq, hresp, hreq);
+                               if(tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) {
+                                       CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq));
+                                       if(httpChecker.notCadi(cw, hresp)) {
+                                               oauthFilter.doFilter(cw,response,chain);
+                                       }
+                               }                                               
+                       }
+               } catch (ClassCastException e) {
+                       throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);
+               }
+       }
+
+
+       /** 
+        * 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) 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/aaf/cadi/filter/CadiHTTPManip.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiHTTPManip.java
new file mode 100644 (file)
index 0000000..094c21b
--- /dev/null
@@ -0,0 +1,210 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Connector;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Taf;
+import org.onap.aaf.cadi.TrustChecker;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.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_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 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 {
+               synchronized(CADI) {
+                       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.INFO,"Authenticated: %s from %s:%d", 
+                                               tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort());
+                               break;
+                       case TRY_AUTHENTICATING:
+                               switch (tresp.authenticate()) {
+                                       case IS_AUTHENTICATED:
+                                               access.printf(Level.INFO,"Authenticated: %s from %s:%d", 
+                                                               tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort());
+                                               break;
+                                       case HTTP_REDIRECT_INVOKED:
+                                               access.log(Level.INFO,"Authenticating via redirection: ", tresp.desc());
+                                               break;
+                                       case NO_FURTHER_PROCESSING:
+                                               access.printf(Level.AUDIT,"Authentication Failure: %s from %s:%d"
+                                                               , tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort());
+                                               hresp.sendError(403, tresp.desc()); // Forbidden
+                                               break;
+
+                                       default:
+                                               access.printf(Level.AUDIT,"No TAF will authorize for request from %s:%d"
+                                                               , hreq.getRemoteAddr(), hreq.getRemotePort());
+                                               hresp.sendError(403, tresp.desc()); // Forbidden
+                               }
+                               break;
+                       case NO_FURTHER_PROCESSING:
+                               access.printf(Level.AUDIT,"Authentication Failure: %s from %s:%d", 
+                                               tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort());
+                               hresp.sendError(403, "Access Denied"); // FORBIDDEN
+                               break;
+                       default:
+                               access.printf(Level.AUDIT,"No TAF will authorize for request from %s:%d"
+                                               , hreq.getRemoteAddr(), hreq.getRemotePort());
+                               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/aaf/cadi/filter/FCGet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/FCGet.java
new file mode 100644 (file)
index 0000000..9c4cca1
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.config.Get;
+
+/*
+ * A private method to query the Filter config and if not exists, return the default.  This
+ * cleans up the initialization code.
+ */
+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/aaf/cadi/filter/MapPermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapPermConverter.java
new file mode 100644 (file)
index 0000000..052b9ff
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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<String,String>();
+       }
+
+       /**
+        * 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/aaf/cadi/filter/NullPermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/NullPermConverter.java
new file mode 100644 (file)
index 0000000..211a4bf
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+
+/**
+ * A NullPermConverter 
+ * 
+ * Obey the PermConverter Interface, but passed in "minimal" String is not converted.
+ * 
+ * @author Jonathan
+ *
+ */
+public class NullPermConverter implements PermConverter {
+
+       private NullPermConverter() {}
+       private static final NullPermConverter singleton = new NullPermConverter();
+       public static NullPermConverter singleton() {return singleton;}
+
+       public String convert(String minimal) {
+               return minimal;
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java
new file mode 100644 (file)
index 0000000..c508a5c
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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 ServletContext context;
+       private String aaf_type;
+       private String not_authorized_msg;
+       private final Log log;
+
+       /**
+        * 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('|');
+               
+               aaf_type = sb.toString();
+
+               attr = context.getAttribute(Config.PATHFILTER_NOT_AUTHORIZED_MSG);
+               if(attr==null) {
+                       not_authorized_msg = "Forbidden - Not Authorized to access this Path";
+               } else {
+                       not_authorized_msg = 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 = aaf_type+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,not_authorized_msg);
+               }
+       }
+
+       /**
+        * Containers call "destroy" when time to cleanup 
+        */
+       public void destroy() {
+               log.info("PathFilter destroyed.");
+       }
+
+
+
+}
+
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java
new file mode 100644 (file)
index 0000000..bb97894
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/filter/RolesAllowed.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/RolesAllowed.java
new file mode 100644 (file)
index 0000000..5f709f1
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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====================================================
+ *
+ */
+
+/**
+ * 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.aaf.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/aaf/cadi/filter/ServletImpl.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/ServletImpl.java
new file mode 100644 (file)
index 0000000..02c2600
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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====================================================
+ *
+ */
+
+/**
+ * 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.aaf.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 javax.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/aaf/cadi/lur/ConfigPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/ConfigPrincipal.java
new file mode 100644 (file)
index 0000000..43dd101
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import org.onap.aaf.cadi.GetCred;
+import org.onap.aaf.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/aaf/cadi/lur/EpiLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/EpiLur.java
new file mode 100644 (file)
index 0000000..2813dca
--- /dev/null
@@ -0,0 +1,169 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.aaf.cadi.CachingLur;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.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.aaf.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/aaf/cadi/lur/LocalLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalLur.java
new file mode 100644 (file)
index 0000000..c1a27fa
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.AbsUserCache;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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 userProperty
+        * @param groupProperty
+        * @param decryptor
+        * @throws IOException
+        */
+       public LocalLur(Access access, String userProperty, String groupProperty) throws IOException {
+               super(access, 0, 0, Integer.MAX_VALUE);  // data doesn't expire
+               supportedRealm = access.getProperty(Config.BASIC_REALM, "localized");
+               supportingGroups = new TreeSet<String>();
+               
+               if(userProperty!=null) {
+                       // For each User name...
+                       for(String user : userProperty.trim().split(SEMI)) {
+                               String[] us = user.split(COLON,2);
+                               String[] userpass = us[0].split(PERCENT,2);
+                               String u;
+                               User<LocalPermission> usr;
+                               if(userpass.length>1) {
+                                       if(userpass.length>0 && userpass[0].indexOf('@')<0) {
+                                               userpass[0]=userpass[0] + '@' + access.getProperty(Config.AAF_DEFAULT_REALM,Config.getDefaultRealm());
+                                       }
+
+                                       u = userpass[0];
+                                       byte[] pass = access.decrypt(userpass[1], true).getBytes();
+                                       usr = new User<LocalPermission>(new ConfigPrincipal(u, pass));
+                               } else {
+                                       u = us[0];
+                                       usr = new User<LocalPermission>(new ConfigPrincipal(u, (byte[])null));
+                               }
+                               addUser(usr);
+                               access.log(Level.INIT, "Local User:",usr.principal);
+                               
+                               if(us.length>1) {
+                                       Map<String, Permission> newMap = usr.newMap();
+                                       for(String group : us[1].split(COMMA)) {
+                                               supportingGroups.add(group);
+                                               usr.add(newMap,new LocalPermission(group));
+                                       }
+                                       usr.setMap(newMap);
+                               }
+                       }
+               }
+               if(groupProperty!=null) {
+                       // For each Group name...
+                       for(String group : groupProperty.trim().split(SEMI)) {
+                               String[] gs = group.split(COLON,2);
+                               if(gs.length>1) {
+                                       supportingGroups.add(gs[0]);
+                                       LocalPermission p = new LocalPermission(gs[0]);
+                                       // Add all users (known by comma separators)    
+                                       
+                                       for(String grpMem : gs[1].split(COMMA)) {
+                                               // look for password, if so, put in passMap
+                                               String[] userpass = grpMem.split(PERCENT,2);
+                                               if(userpass.length>0 && userpass[0].indexOf('@')<0) {
+                                                       userpass[0]=userpass[0] + '@' + access.getProperty(Config.AAF_DEFAULT_REALM,Config.getDefaultRealm());
+                                               }
+                                               User<LocalPermission> usr = null;
+                                               if(userpass.length>1) {
+                                                       byte[] pass = access.decrypt(userpass[1], true).getBytes();
+                                                       usr = getUser(userpass[0],pass);
+                                                       if(usr==null)addUser(usr=new User<LocalPermission>(new ConfigPrincipal(userpass[0],pass)));
+                                                       else usr.principal=new ConfigPrincipal(userpass[0],pass);
+                                               } else {
+                                                       addUser(usr=new User<LocalPermission>(new ConfigPrincipal(userpass[0],(byte[])null)));
+                                               }
+                                               usr.add(p);
+                                               access.log(Level.INIT, "Local User:",usr.principal);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       public boolean validate(String user, CredVal.Type type, byte[] cred, Object state) {
+               User<LocalPermission> usr = getUser(user,cred);
+               switch(type) {
+                       case PASSWORD:
+                               // covers null as well as bad pass
+                               if(usr!=null && cred!=null && usr.principal instanceof ConfigPrincipal) {
+                                       return Hash.isEqual(cred,((ConfigPrincipal)usr.principal).getCred());
+                               }
+                               break;
+               }
+               return false;
+       }
+
+       //      @Override
+       public boolean fish(Principal bait, Permission pond) {
+               if(pond == null) {
+                       return false;
+               }
+               if(handles(bait) && pond instanceof LocalPermission) { // local Users only have LocalPermissions
+                               User<LocalPermission> user = getUser(bait);
+                               return user==null?false:user.contains((LocalPermission)pond);
+                       }
+               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.aaf.cadi.Lur#handles(java.security.Principal)
+        */
+       @Override
+       public boolean handles(Principal principal) {
+               return principal!=null && principal.getName().endsWith(supportedRealm);
+       }
+
+//     public boolean supports(String userName) {
+//             return userName!=null && userName.endsWith(supportedRealm);
+//     }
+//
+       public boolean handlesExclusively(Permission pond) {
+               return supportingGroups.contains(pond.getKey());
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String)
+        */
+       @Override
+       public Permission createPerm(String p) {
+               return new LocalPermission(p);
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java
new file mode 100644 (file)
index 0000000..8d6f969
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur;
+
+import org.onap.aaf.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/aaf/cadi/lur/NullLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/NullLur.java
new file mode 100644 (file)
index 0000000..1e44726
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.lur;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.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.aaf.cadi.Lur#createPerm(java.lang.String)
+        */
+       @Override
+       public Permission createPerm(String p) {
+               return NULL;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.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';
+       }
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java
new file mode 100644 (file)
index 0000000..6a49401
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.GetCred;
+import org.onap.aaf.cadi.Symm;
+
+public class BasicPrincipal extends BearerPrincipal implements GetCred {
+       private static byte[] basic = "Basic ".getBytes();
+
+       private String name = null;
+       private String shortName = null;
+       private byte[] cred = null;
+       
+       private long created;
+
+       public BasicPrincipal(String content,String domain) 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;
+                       name = name + '@' + domain;
+               }
+       }
+       
+       public BasicPrincipal(BasicCred bc, String domain) {
+               name = bc.getUser();
+               cred = bc.getCred();
+       }
+
+       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 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 null;  // personalName not available with Basic Auth
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java
new file mode 100644 (file)
index 0000000..ea0ff2f
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/principal/CachedBasicPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/CachedBasicPrincipal.java
new file mode 100644 (file)
index 0000000..68229d3
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.principal;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.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/aaf/cadi/principal/Kind.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/Kind.java
new file mode 100644 (file)
index 0000000..bb6dc67
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/principal/OAuth2FormPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/OAuth2FormPrincipal.java
new file mode 100644 (file)
index 0000000..1df2bd3
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/principal/StringTagLookup.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/StringTagLookup.java
new file mode 100644 (file)
index 0000000..a392716
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.principal;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.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/aaf/cadi/principal/TaggedPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TaggedPrincipal.java
new file mode 100644 (file)
index 0000000..a3b07c6
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.principal;
+
+import java.security.Principal;
+
+import org.onap.aaf.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/aaf/cadi/principal/TrustPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TrustPrincipal.java
new file mode 100644 (file)
index 0000000..0908331
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.principal;
+
+import java.security.Principal;
+
+import org.onap.aaf.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/aaf/cadi/principal/UnAuthPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/UnAuthPrincipal.java
new file mode 100644 (file)
index 0000000..52f78e8
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/principal/X509Principal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/X509Principal.java
new file mode 100644 (file)
index 0000000..16f6217
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.principal;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.regex.Pattern;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.GetCred;
+
+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 TagLookup tagLookup;
+       private byte[] content;  
+
+       public X509Principal(String identity, X509Certificate cert) {
+               name = identity;
+               content = null;
+               this.cert = cert;
+               tagLookup = null;
+       }
+
+       public X509Principal(String identity, X509Certificate cert, byte[] content) {
+               name = identity;
+               this.content = content;
+               this.cert = cert;
+               tagLookup = null;
+       }
+
+       public X509Principal(X509Certificate cert, byte[] content) 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;
+               tagLookup = null;
+       }
+       
+       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";
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java
new file mode 100644 (file)
index 0000000..a2fc730
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+/**
+ * AbsTafResp
+ * 
+ * Base class for TafResp (TAF Response Objects)
+ * 
+ * @author Jonathan
+ *
+ */
+public abstract class AbsTafResp implements TafResp {
+
+       protected final String desc;
+       protected final TaggedPrincipal principal;
+       protected final Access access;
+
+       /**
+        * 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 principal
+        * @param description
+        */
+       public AbsTafResp(Access access, TaggedPrincipal principal, String description) {
+               this.access = access;
+               this.principal = principal;
+               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;
+       }
+
+       /**
+        * 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.aaf.cadi.taf.TafResp#isFailedAttempt()
+        */
+       public boolean isFailedAttempt() {
+               return false;
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java
new file mode 100644 (file)
index 0000000..d772d49
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.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/aaf/cadi/taf/HttpEpiTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpEpiTaf.java
new file mode 100644 (file)
index 0000000..e575be1
--- /dev/null
@@ -0,0 +1,197 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.net.URI;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.TrustChecker;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Taf.LifeForm;
+
+/**
+ * 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 
+//             trustChainProp = access.getProperty(Config.CADI_TRUST_PROP, Config.CADI_TRUST_PROP_DEFAULT);
+
+               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, firstTry = null;
+               List<Redirectable> redirectables = null;
+               List<TafResp> trlog = access.willLog(Level.DEBUG)?new ArrayList<TafResp>():null;
+               try {
+                       for(HttpTaf taf : tafs) {
+                               tresp = taf.validate(reading, req, resp);
+                               if(trlog!=null) {
+                                       trlog.add(tresp);
+                               }
+                               switch(tresp.isAuthenticated()) {
+                                       case TRY_ANOTHER_TAF:
+                                               break; // and loop
+                                       case TRY_AUTHENTICATING:
+                                               if(tresp instanceof Redirectable) {
+                                                       if(redirectables==null) {
+                                                               redirectables = new ArrayList<Redirectable>();
+                                                       }
+                                                       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 {             
+                       if(trlog!=null) {
+                               for( TafResp tr : trlog) {
+                                       access.log(Level.DEBUG, tr.desc());
+                               }
+                       }
+               }
+               
+               // 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.
+               
+               String auth = req.getParameter("Authentication");
+               if(auth!=null) {
+                       if("BasicAuth".equals(auth)) {
+                               return LifeForm.SBLF;
+                       }
+               }
+               // 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);
+                       switch(resp) {
+                               case NOT_MINE:
+                                       break;
+                               default:
+                                       return resp;
+                       }
+               }
+               return Resp.NOT_MINE;
+       }
+
+       /**
+        * 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/aaf/cadi/taf/HttpTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpTaf.java
new file mode 100644 (file)
index 0000000..9484458
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.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/aaf/cadi/taf/LoginPageTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/LoginPageTafResp.java
new file mode 100644 (file)
index 0000000..9c9cbc2
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Locator;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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, null, "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> redir) {
+               if(locator!=null) {
+                       try {
+                               Item item = locator.best();
+                               URI uri = locator.get(item);
+                               if(uri!=null) {
+                                       StringBuilder sb = new StringBuilder(uri.toString());
+                                       String query = uri.getQuery();
+                                       boolean first = query==null || query.length()==0;
+                                       int count=0;
+                                       for(Redirectable t : redir) {
+                                               if(first) {
+                                                       sb.append('?');
+                                                       first=false;
+                                               }
+                                               else sb.append('&');
+                                               sb.append(t.get());
+                                               ++count;
+                                       }
+                                       if(count>0)return new LoginPageTafResp(access, resp, sb.toString());
+                               }
+                       } catch (Exception e) {
+                               access.log(e, "Error deriving Login Page location");
+                       }
+               } else if(!redir.isEmpty()) { 
+                       access.log(Level.DEBUG,"LoginPage Locator is not configured. Taking first Redirectable Taf");
+                       return redir.get(0);
+               }
+               return NullTafResp.singleton();
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java
new file mode 100644 (file)
index 0000000..e8293fa
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.Taf;
+import org.onap.aaf.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/aaf/cadi/taf/NullTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTafResp.java
new file mode 100644 (file)
index 0000000..20fc944
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.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;
+       }
+
+       public Access getAccess() {
+               return Access.NULL;
+       }
+
+       /* (non-Javadoc)
+        * @see org.onap.aaf.cadi.taf.TafResp#isFailedAttempt()
+        */
+       public boolean isFailedAttempt() {
+               return true;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java
new file mode 100644 (file)
index 0000000..f496581
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+/**
+ * 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 desc;
+
+       public PuntTafResp(String name, String explanation) {
+               desc = name + " is 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;
+       }
+
+       public Access getAccess() {
+               return NullTafResp.singleton().getAccess();
+       }
+
+       public boolean isFailedAttempt() {
+               return false;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java
new file mode 100644 (file)
index 0000000..8dc5c11
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/taf/TafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TafResp.java
new file mode 100644 (file)
index 0000000..a679d99
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.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();
+
+       /**
+        * 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();
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java
new file mode 100644 (file)
index 0000000..24a79cf
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+public class TrustNotTafResp implements TafResp {
+       private final TafResp delegate;
+       private final String desc;
+       
+       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();
+       }
+
+       @Override
+       public Access getAccess() {
+               return delegate.getAccess();
+       }
+
+       @Override
+       public boolean isFailedAttempt() {
+               return true;
+       }
+       
+       public String toString() {
+               return desc();
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java
new file mode 100644 (file)
index 0000000..bc5e8db
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+
+public class TrustTafResp implements TafResp {
+       private final TafResp delegate;
+       private final TaggedPrincipal principal;
+       private final String desc;
+       
+       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;
+       }
+
+       @Override
+       public Access getAccess() {
+               return delegate.getAccess();
+       }
+
+       @Override
+       public boolean isFailedAttempt() {
+               return delegate.isFailedAttempt();
+       }
+       
+       public String toString() {
+               return principal.getName() + " by trust of " + desc();
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java
new file mode 100644 (file)
index 0000000..6d516f0
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf.basic;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CredVal;
+import org.onap.aaf.cadi.Taf;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.CredVal.Type;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf;
+
+/**
+ * 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 boolean warn;
+       private long timeToLive;
+       
+       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;
+       }
+
+       /**
+        * 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);
+                               // ONLY FOR Last Ditch DEBUGGING... 
+                               // access.log(Level.WARN,bp.getName() + ":" + new String(bp.getCred()));
+                               
+                               if(rbac.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,null,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");
+               if(authz != null && authz.startsWith("Basic ")) {
+                       if(warn&&!req.isSecure()) {
+                               access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
+                       }
+                       try {
+                               CachedBasicPrincipal ba = new CachedBasicPrincipal(this,authz,realm,timeToLive);
+                               if(DenialOfServiceTaf.isDeniedID(ba.getName())!=null) {
+                                       return DenialOfServiceTaf.respDenyID(access,ba.getName());
+                               }
+
+                               // ONLY FOR Last Ditch DEBUGGING... 
+                               // access.log(Level.WARN,ba.getName() + ":" + new String(ba.getCred()));
+                               if(rbac.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,null,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,null,msg, RESP.TRY_AUTHENTICATING, resp, realm,true);
+                       }
+               }
+               return new BasicHttpTafResp(access,null,"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();
+       }
+
+       @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/aaf/cadi/taf/basic/BasicHttpTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTafResp.java
new file mode 100644 (file)
index 0000000..c17797b
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf.basic;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.taf.AbsTafResp;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class BasicHttpTafResp extends AbsTafResp implements TafResp {
+       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,principal, 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/aaf/cadi/taf/cert/CertIdentity.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/CertIdentity.java
new file mode 100644 (file)
index 0000000..0da41b8
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf.cert;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.onap.aaf.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/aaf/cadi/taf/cert/X509HttpTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509HttpTafResp.java
new file mode 100644 (file)
index 0000000..b7f63b8
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf.cert;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.taf.AbsTafResp;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class X509HttpTafResp extends AbsTafResp implements TafResp {
+       private RESP status;
+       
+       public X509HttpTafResp(Access access, TaggedPrincipal principal, String description, RESP status) {
+               super(access, 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/aaf/cadi/taf/cert/X509Taf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509Taf.java
new file mode 100644 (file)
index 0000000..b5ed7ad
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Taf.LifeForm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.config.SecurityInfo;
+import org.onap.aaf.cadi.config.SecurityInfoC;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.principal.X509Principal;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+import org.onap.aaf.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;
+
+       static {
+               try {
+                       certFactory = CertificateFactory.getInstance("X.509");
+                       messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone
+                       tmf = TrustManagerFactory.getInstance(SecurityInfoC.SslKeyManagerFactoryAlgorithm);
+               } 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<String>();
+               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.aaf.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
+                               if(cadiIssuers.contains(certarr[0].getIssuerDN().toString())) {
+                                       String subject = certarr[0].getSubjectDN().getName();
+                                       // avoiding extra object creation, since this is validated EVERY transaction with a Cert
+                                       int at = subject.indexOf('@');
+                                       if(at>=0) {
+                                               int start = subject.lastIndexOf(',', at);
+                                               if(start<0) {
+                                                       start = 0;
+                                               }
+                                               int end = subject.indexOf(',', at);
+                                               if(end<0) {
+                                                       end=subject.length();
+                                               }
+                                               int temp;
+                                               if(((temp=subject.indexOf("OU=",start))>=0 && temp<end) || 
+                                                  ((temp=subject.indexOf("CN=",start))>=0 && temp<end)) {
+                                                       String[] sa = Split.splitTrim(':', subject, temp+3,end);
+                                                       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), 
+                                                                               "X509Taf validated " + sa[0] + (sa.length<2?"":" for aaf_env " + env ), RESP.IS_AUTHENTICATED);
+                                                       }
+                                               }
+                                               
+                                       }
+                               }
+                       }
+               
+
+                       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) {
+                                       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;
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java
new file mode 100644 (file)
index 0000000..5eca0f2
--- /dev/null
@@ -0,0 +1,374 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Taf.LifeForm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.taf.HttpTaf;
+import org.onap.aaf.cadi.taf.PuntTafResp;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.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(deniedID!=null) {
+                       return deniedID.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<String,Counter>();
+                       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));
+                               if(deniedIP==null) {
+                                       deniedIP=new HashMap<String,Counter>();
+                               }
+
+                               try {
+                                       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<String,Counter>();
+                       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));
+                               if(deniedID==null) {
+                                       deniedID=new HashMap<String,Counter>();
+                               }
+                               try {
+                                       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<String>(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/aaf/cadi/taf/dos/DenialOfServiceTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTafResp.java
new file mode 100644 (file)
index 0000000..b156392
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.taf.dos;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.taf.AbsTafResp;
+
+public class DenialOfServiceTafResp extends AbsTafResp  {
+       private RESP ect;  // Homage to Arethra Franklin
+
+       public DenialOfServiceTafResp(Access access, RESP resp, String description ) {
+               super(access, null, 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;
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java
new file mode 100644 (file)
index 0000000..74bf805
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/FQI.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/FQI.java
new file mode 100644 (file)
index 0000000..b953c87
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.toString();
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java
new file mode 100644 (file)
index 0000000..7b04942
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/MaskFormatException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MaskFormatException.java
new file mode 100644 (file)
index 0000000..7dd51c0
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/MyConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MyConsole.java
new file mode 100644 (file)
index 0000000..2312d00
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/NetMask.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/NetMask.java
new file mode 100644 (file)
index 0000000..fccb04f
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/Pool.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java
new file mode 100644 (file)
index 0000000..d61aee2
--- /dev/null
@@ -0,0 +1,380 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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====================================================
+ *
+ */
+
+/*
+ * Pool
+ * 
+ * Author: Jonathan
+ * 5/27/2011
+ */
+package org.onap.aaf.cadi.util;
+
+import java.util.LinkedList;
+
+import org.onap.aaf.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.
+        */
+       private static final int MAX_RANGE = 6; // safety
+
+       /**
+        * 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 exist, to avoid asking list.
+        */
+       private int count;
+
+       /**
+        * Spares are those Object that are primed and ready to go.
+        */
+       private int spares;
+
+       /**
+        * 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;
+
+       /**
+        * 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 = spares = 0;
+               this.creator = creator;
+               list = new LinkedList<Pooled<T>>();
+               logger = Log.NULL;
+       }
+       
+       /**
+        * Attach Pool Logging activities to any other Logging Mechanism.
+        * @param logger
+        */
+       public void setLogger(Log logger) {
+               this.logger = logger;
+       }
+       
+       public void log(Object ...objects) {
+               logger.log(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
+        * 
+        * @param lt
+        * @param prime
+        * @throws CadiException 
+        */
+       public void prime(int prime) throws CadiException  {
+               for (int i = 0; i < prime; ++i) {
+                       Pooled<T> pt = new Pooled<T>(creator.create(), this);
+                       synchronized (list) {
+                               list.addFirst(pt);
+                               ++count;
+                       }
+               }
+
+       }
+
+       /**
+        * 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.
+        */
+       public void drain() {
+               synchronized (list) {
+                       for (int i = 0; i < list.size(); ++i) {
+                               Pooled<T> pt = list.remove();
+                               creator.destroy(pt.content);
+                               logger.log("Pool drained ", creator.toString());
+                       }
+                       count = spares = 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) {
+                       if (list.isEmpty()) {
+                               pt = null;
+                       } else {
+                               pt = list.removeLast();
+                               --count;
+                               creator.reuse(pt.content);
+                       }
+               }
+               if (pt == null) {
+                       if (spares < max_range)
+                               ++spares;
+                       pt = new Pooled<T>(creator.create(), this);
+               } else {
+                       if (spares > 1)
+                               --spares;
+               }
+               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 (Pooled<T> t : list) {
+                               if (!creator.isValid(t.content)) {
+                                       rv = false;
+                                       t.toss();
+                                       list.remove(t);
+                               }
+                       }
+               }
+               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> used) {
+               if (count < spares) {
+                       synchronized (list) {
+                               list.addFirst(used);
+                               ++count;
+                       }
+                       logger.log("Pool recovered ", creator);
+               } else {
+                       logger.log("Pool destroyed ", creator);
+                       creator.destroy(used.content);
+               }
+               return false;
+       }
+
+       /**
+        * 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);
+       }
+
+       public interface Log {
+               public void log(Object ... o);
+               
+               public final static Log NULL = new Log() {
+                       @Override
+                       public void log(Object ... o) {
+                       }
+               };
+       }
+       /**
+        * 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;
+                       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.creator.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;
+                       }
+               }
+       }
+
+       /**
+        * Get the maximum number of spare objects allowed at any moment
+        * 
+        * @return
+        */
+       public int getMaxRange() {
+               return max_range;
+       }
+
+       /**
+        * Set a Max Range for numbers of spare objects waiting to be used.
+        * 
+        * No negative numbers are allowed
+        * 
+        * @return
+        */
+       public void setMaxRange(int max_range) {
+               // Do not allow negative numbers
+               this.max_range = Math.max(0, max_range);
+       }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java
new file mode 100644 (file)
index 0000000..3fa9a3f
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 {
+         public static String[] split(char c, String value) {
+                 return split(c,value,0,value.length());
+         }
+
+         public static String[] split(char c, String value, int start, int end) {
+                 if(value==null) {
+                         return new String[0];
+                 }
+
+                 // 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 new String[0];
+                 }
+
+                 // 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=0;
+                         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) {
+                 return splitTrim(c,value,0,value.length());
+         }
+
+         public static String[] splitTrim(char c, String value, int size) {
+                 if(value==null) {
+                         return new String[0];
+                 }
+
+                 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/aaf/cadi/util/SubStandardConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/SubStandardConsole.java
new file mode 100644 (file)
index 0000000..8d52811
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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 {
+       BufferedReader 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);
+                       return br.readLine().toCharArray();
+               } catch (IOException e) {
+                       System.err.println("uh oh...");
+                       return new char[0];
+               }
+       }
+
+       @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/aaf/cadi/util/TheConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/TheConsole.java
new file mode 100644 (file)
index 0000000..4c5d35b
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/util/UserChainManip.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/UserChainManip.java
new file mode 100644 (file)
index 0000000..a8c0690
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.util;
+
+import org.onap.aaf.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/aaf/cadi/util/Vars.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Vars.java
new file mode 100644 (file)
index 0000000..55470f9
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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) {
+               String[] array = new String[vars.size()];
+               StringBuilder sb = new StringBuilder();
+               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 String ... 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/aaf/cadi/wsse/Action.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Action.java
new file mode 100644 (file)
index 0000000..dff18ac
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/wsse/Match.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Match.java
new file mode 100644 (file)
index 0000000..2582bc1
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/wsse/WSSEParser.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/WSSEParser.java
new file mode 100644 (file)
index 0000000..9e36c11
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.wsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.stream.XMLStreamException;
+
+import org.onap.aaf.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;
+       //private XMLInputFactory inputFactory;
+
+       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
+               //inputFactory = XMLInputFactory.newInstance();
+       }
+       
+       public XMLStreamException parse(BasicCred bc, InputStream is) throws IOException {
+               try {
+                       parseTree.onMatch(bc, new XReader(is));
+                       return null;
+               } catch (XMLStreamException e) {
+                       return e;
+               }
+       }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java
new file mode 100644 (file)
index 0000000..12de366
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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/aaf/cadi/wsse/XReader.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XReader.java
new file mode 100644 (file)
index 0000000..4e5048b
--- /dev/null
@@ -0,0 +1,417 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.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.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;
+               }
+               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<String,String>();
+                                               if(nss!=null)newnss.putAll(nss);
+                                       }
+                                       newnss.put(tag.name, tag.value);
+                               }
+                       }
+               }
+               return newnss==null?(nss==null?new HashMap<String,String>():nss):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<Tag>();
+                       }
+                       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/.gitignore b/cadi/core/src/test/java/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java
new file mode 100644 (file)
index 0000000..4fb9242
--- /dev/null
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+
+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.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.junit.Test;
+import org.onap.aaf.cadi.AES;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+import org.junit.Before;
+
+public class JU_AES {
+       private AES aes;
+       private ByteArrayInputStream baisEncrypt;
+       private ByteArrayInputStream baisDecrypt;
+       private ByteArrayOutputStream baosEncrypt;
+       private ByteArrayOutputStream baosDecrypt;
+
+       @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);
+       }
+
+       @Test
+       public void newKeyTest() throws Exception {
+               SecretKey secretKey = AES.newKey();
+               assertEquals(secretKey.getAlgorithm(), 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);
+               assertEquals(orig, new String(decrypted));
+               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 = "test/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());
+               assertEquals(orig, output);
+
+               Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec");
+               aeskeySpec_field.setAccessible(true);
+               aeskeySpec_field.set(aes, null);
+
+               assertNull(aes.inputStream(baisEncrypt, true));
+       }
+
+       @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());
+               assertEquals(orig, output);
+
+               Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec");
+               aeskeySpec_field.setAccessible(true);
+               aeskeySpec_field.set(aes, null);
+
+               assertNull(aes.outputStream(baosEncrypt, true));
+       }
+
+       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/aaf/cadi/test/JU_Access.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Access.java
new file mode 100644 (file)
index 0000000..d62144c
--- /dev/null
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.IOException;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.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(57617));
+               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/aaf/cadi/test/JU_Base64.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Base64.java
new file mode 100644 (file)
index 0000000..801259d
--- /dev/null
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.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.aaf.cadi.Symm;
+import org.onap.aaf.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/aaf/cadi/test/JU_BufferedCadiWrap.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedCadiWrap.java
new file mode 100644 (file)
index 0000000..172270d
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.test;
+
+import javax.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/aaf/cadi/test/JU_BufferedServletInputStream.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedServletInputStream.java
new file mode 100644 (file)
index 0000000..66ac361
--- /dev/null
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.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.aaf.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/aaf/cadi/test/JU_CadiExceptionTest.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiExceptionTest.java
new file mode 100644 (file)
index 0000000..03b2037
--- /dev/null
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class JU_CadiExceptionTest {
+       @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/aaf/cadi/test/JU_CadiWrapTest.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiWrapTest.java
new file mode 100644 (file)
index 0000000..14b2999
--- /dev/null
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.when;
+
+import java.security.Principal;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CachingLur;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.filter.MapPermConverter;
+import org.onap.aaf.cadi.lur.EpiLur;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.taf.TafResp;
+
+public class JU_CadiWrapTest {
+       
+       @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);
+       }
+
+       @Test
+       public void testInstantiate() throws CadiException {
+               Access a = new PropAccess();
+               when(tafResp.getAccess()).thenReturn(a);
+               
+               lur.fishAll(isA(Principal.class), 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");
+               
+               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/aaf/cadi/test/JU_Capacitor.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Capacitor.java
new file mode 100644 (file)
index 0000000..e9bcecc
--- /dev/null
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.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.aaf.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/aaf/cadi/test/JU_CmdLine.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CmdLine.java
new file mode 100644 (file)
index 0000000..5da67ce
--- /dev/null
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.mockito.Matchers.*;
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+import org.mockito.*;
+import static org.mockito.Mockito.*;
+
+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.onap.aaf.cadi.CmdLine;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.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.access = new PropAccess(p);
+
+               password = "password";
+               keyfile = "test/keyfile";
+
+               FileInputStream fis = new FileInputStream(keyfile);
+               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 regurgitateTest() {
+               // TODO: We may still want to remove the regurgitate functionality
+               // from the CmdLine - Ian
+               fail("Tests not yet implemented");
+       }
+
+       @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 + "\n"));
+       }
+
+       @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 + "\n"));
+       }
+
+       @Test
+       public void md5Test() throws Exception {
+               CmdLine.main(new String[]{"md5", quickBrownFoxPlain});
+               assertThat(outContent.toString(), is(quickBrownFoxMD5 + "\n"));
+       }
+
+       @Test
+       public void sha256Test() throws Exception {
+               CmdLine.main(new String[]{"sha256", quickBrownFoxPlain});
+               assertThat(outContent.toString(), is(quickBrownFoxSHA256 + "\n"));
+
+               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";
+               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 expected = 
+                       "Usage: java -jar <this jar> ...\n" +
+                       "  keygen [<keyfile>]                     (Generates Key on file, or Std Out)\n" +
+                       "  digest [<passwd>|-i|] <keyfile>        (Encrypts Password with \"keyfile\"\n" +
+                       "                                          if passwd = -i, will read StdIin\n" +
+                       "                                          if passwd is blank, will ask securely)\n" +
+                       "  passgen <digits>                       (Generate Password of given size)\n" +
+                       "  urlgen <digits>                        (Generate URL field of given size)\n" +
+                       "  csptest                                (Tests for CSP compatibility)\n" +
+                       "  encode64 <your text>                   (Encodes to Base64)\n" +
+                       "  decode64 <base64 encoded text>         (Decodes from Base64)\n" +
+                       "  encode64url <your text>                (Encodes to Base64 URL charset)\n" +
+                       "  decode64url <base64url encoded text>   (Decodes from Base64 URL charset)\n" +
+                       "  sha256 <text> <salts(s)>               (Digest String into SHA256 Hash)\n" +
+                       "  md5 <text>                             (Digest String into MD5 Hash)\n";
+
+               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/aaf/cadi/test/JU_Hash.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Hash.java
new file mode 100644 (file)
index 0000000..f5c4d87
--- /dev/null
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Hash;
+
+import static org.junit.Assert.*;
+
+import org.junit.BeforeClass;
+
+import static org.hamcrest.CoreMatchers.*;
+
+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)));
+
+        try {
+            // This string doesn't begin with "0x"
+            Hash.fromHex("0X65");
+                       fail("Should have thrown CadiException");
+        } catch (CadiException e) {
+            assertEquals("HexString must start with \"0x\"", e.getMessage());
+        }
+
+        try {
+            // This string has invalid hex characters
+            Hash.fromHex("0xQ");
+                       fail("Should have thrown CadiException");
+        } catch (CadiException e) {
+            // 81 is dec(Q)
+            assertEquals("Invalid char '81' in HexString", e.getMessage());
+        }
+       }
+
+       @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)));
+               assertEquals(numbersDec, new String(Hash.fromHexNo0x(numbersHexNo0x)));
+               byte[] output = Hash.fromHexNo0x("ABC");
+               assertEquals(new String(new byte[] {(byte)0x0A, (byte)0xB0}), new String(output));
+               assertNull(Hash.fromHexNo0x("~~"));
+       }
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java
new file mode 100644 (file)
index 0000000..96cf8e5
--- /dev/null
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.onap.aaf.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/aaf/cadi/test/JU_PropAccess.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_PropAccess.java
new file mode 100644 (file)
index 0000000..d74648a
--- /dev/null
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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 noLogItConstructionTest() throws Exception {
+               // Test for coverage
+               PropAccess prop = new PropAccess((LogIt)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=test/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/aaf/cadi/test/JU_ServletContextAccess.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_ServletContextAccess.java
new file mode 100644 (file)
index 0000000..8531e1d
--- /dev/null
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.ServletContextAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.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 javax.servlet.FilterConfig;
+import javax.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 ConstructorTest() throws Exception {
+               ServletContextAccess sca = new ServletContextAccess(filter_mock);
+       }
+
+       @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/aaf/cadi/test/JU_Symm.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Symm.java
new file mode 100644 (file)
index 0000000..34f528f
--- /dev/null
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+import java.lang.reflect.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class JU_Symm {
+       private Symm defaultSymm;
+
+       @Before
+       public void setup() throws Exception {
+       defaultSymm = new Symm(
+                       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+                       ,76, "Use default!" ,true);
+       }
+
+       @Test
+       public void constructorTest() throws Exception {
+               Symm myCustomSymm = new Symm(
+                       "ACEGIKMOQSUWYacegikmoqsuwy02468+/".toCharArray(), 76, "Default", true);
+               Field convert_field = Symm.class.getDeclaredField("convert");
+               convert_field.setAccessible(true);
+
+               Class<?> Unordered_class = Class.forName("org.onap.aaf.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=test/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/aaf/cadi/test/JU_TrustChecker.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_TrustChecker.java
new file mode 100644 (file)
index 0000000..511c6ee
--- /dev/null
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/JU_User.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_User.java
new file mode 100644 (file)
index 0000000..2568324
--- /dev/null
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.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.aaf.cadi.Permission;
+import org.onap.aaf.cadi.User;
+import org.onap.aaf.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<String,Permission>();
+               
+               assertFalse(user.contains(permission));
+               
+               user.add(newMap, permission);
+               user.setMap(newMap);
+               
+               assertTrue(user.contains(permission));
+               
+               List<Permission> sink = new ArrayList<Permission>();
+               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/aaf/cadi/test/filter/JU_FCGetTest.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/filter/JU_FCGetTest.java
new file mode 100644 (file)
index 0000000..5aa9d79
--- /dev/null
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.filter;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aaf.cadi.PropAccess;
+
+public class JU_FCGetTest {
+
+       @Test
+       public void netYetTested() {
+               fail("Tests not yet implemented");
+       }
+
+//     @Mock
+//     private ServletContext context;
+       
+//     @Mock
+//     private FilterConfig config;
+
+//     @Mock
+//     private PropAccess access = new PropAccess();
+       
+//     @Before
+//     public void setUp() {
+//             MockitoAnnotations.initMocks(this);
+//     }
+       
+//     @Test
+//     public void testGetStringFromDef() {
+//             PropAccess access = new PropAccess();
+               
+//             FCGet fcGet = new FCGet(access, context, config);
+
+//             String user = fcGet.get("user", "DefaultUser", true);
+               
+//             assertEquals(user, "DefaultUser");
+//     }
+
+//     @Test
+//     public void testGetStringFromContext() {
+//             PropAccess access = new PropAccess();
+//             when(context.getInitParameter("user")).thenReturn("ContextUser");
+               
+//             FCGet fcGet = new FCGet(access, context, null);
+
+//             String user = fcGet.get("user", "DefaultUser", true);
+               
+//             assertEquals(user,"ContextUser");
+//     }
+       
+//     @Test
+//     public void testGetStringFromFilter() {
+//             PropAccess access = new PropAccess();
+//             when(config.getInitParameter("user")).thenReturn("FilterUser");
+               
+//             FCGet fcGet = new FCGet(access, null, config);
+
+//             String user = fcGet.get("user", "DefaultUser", true);
+               
+//             assertEquals(user,"FilterUser");
+//     }
+       
+//     @Test
+//     public void testGetStringWithNullContextFilter() {
+               
+//             when(access.getProperty("user", "DefaultUser")).thenReturn(null);
+               
+//             FCGet fcGet = new FCGet(access, null, null);
+
+//             String user = fcGet.get("user", "DefaultUser", true);
+               
+//             assertEquals(user,"DefaultUser");
+//     }
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_ConfigPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_ConfigPrincipal.java
new file mode 100644 (file)
index 0000000..897744e
--- /dev/null
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.lur;
+
+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.aaf.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/aaf/cadi/test/lur/JU_LocalLur.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_LocalLur.java
new file mode 100644 (file)
index 0000000..8388fa5
--- /dev/null
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.lur;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.Lur;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.CredVal.Type;
+import org.onap.aaf.cadi.config.UsersDump;
+import org.onap.aaf.cadi.lur.LocalLur;
+import org.onap.aaf.cadi.lur.LocalPermission;
+
+public class JU_LocalLur {
+
+       @Test
+       public void test() throws IOException {
+               Symm symmetric = Symm.baseCrypt().obtain();
+               LocalLur up;
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               baos.write(Symm.ENC.getBytes());
+               symmetric.enpass("<pass>", baos);
+               PropAccess ta = new PropAccess();
+               Lur ml = up = new LocalLur(ta,"myname:groupA,groupB","admin:myname,yourname;suser:hisname,hername,m1234%"+baos.toString());
+
+               
+//             Permission admin = new LocalPermission("admin");
+//             Permission suser = new LocalPermission("suser");
+//             
+//             // Check User fish
+//             assertTrue(ml.fish(new JUPrincipal("myname"),admin));
+//             assertTrue(ml.fish(new JUPrincipal("hisname"),admin));
+//             assertFalse(ml.fish(new JUPrincipal("noname"),admin));
+//             assertTrue(ml.fish(new JUPrincipal("itsname"),suser));
+//             assertTrue(ml.fish(new JUPrincipal("hername"),suser));
+//             assertFalse(ml.fish(new JUPrincipal("myname"),suser));
+//             
+//             // Check validate password
+//             assertTrue(up.validate("m1234",Type.PASSWORD, "<pass>".getBytes()));
+//             assertFalse(up.validate("m1234",Type.PASSWORD, "badPass".getBytes()));
+//             
+               // Check fishAll
+               Set<String> set = new TreeSet<String>();
+               List<Permission> perms = new ArrayList<Permission>();
+               ml.fishAll(new JUPrincipal("myname"), perms);
+               for(Permission p : perms) {
+                       set.add(p.getKey());
+               }
+//             assertEquals("[admin, groupA, groupB]",set.toString());
+               UsersDump.write(System.out, up);
+               System.out.flush();
+               
+       }
+       
+       // Simplistic Principal for testing purposes
+       private static class JUPrincipal implements Principal {
+               private String name;
+               public JUPrincipal(String name) {
+                       this.name = name;
+               }
+//             @Override
+               public String getName() {
+                       return name;
+               }
+       }
+
+
+
+       
+       
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_LocalPermission.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_LocalPermission.java
new file mode 100644 (file)
index 0000000..abc178a
--- /dev/null
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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,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.aaf.cadi.test.lur;
+
+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.aaf.cadi.lur.LocalPermission;
+import org.onap.aaf.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/aaf/cadi/test/lur/JU_NullLur.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/lur/JU_NullLur.java
new file mode 100644 (file)
index 0000000..d5740b3
--- /dev/null
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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,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.aaf.cadi.test.lur;
+
+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.aaf.cadi.Permission;
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_BasicPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_BasicPrincipal.java
new file mode 100644 (file)
index 0000000..0faaca1
--- /dev/null
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.StringTagLookup;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.principal.TaggedPrincipal.TagLookup;
+
+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(nullValue()));
+
+               // 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/aaf/cadi/test/principal/JU_CachedBasicPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_CachedBasicPrincipal.java
new file mode 100644 (file)
index 0000000..39da6c0
--- /dev/null
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+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.lang.reflect.Field;
+import java.util.Date;
+
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.CachedPrincipal;
+import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
+import org.onap.aaf.cadi.principal.StringTagLookup;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.principal.TaggedPrincipal.TagLookup;
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_Kind.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_Kind.java
new file mode 100644 (file)
index 0000000..c66539c
--- /dev/null
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.onap.aaf.cadi.principal.BasicPrincipal;
+import org.onap.aaf.cadi.principal.Kind;
+import org.onap.aaf.cadi.principal.OAuth2FormPrincipal;
+import org.onap.aaf.cadi.principal.TrustPrincipal;
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_OAuth2FormPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_OAuth2FormPrincipal.java
new file mode 100644 (file)
index 0000000..a7c14aa
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_StringTagLookup.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_StringTagLookup.java
new file mode 100644 (file)
index 0000000..ff1f3f8
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_TaggedPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_TaggedPrincipal.java
new file mode 100644 (file)
index 0000000..4674db7
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.cadi.principal.TaggedPrincipal.TagLookup;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_TrustPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_TrustPrincipal.java
new file mode 100644 (file)
index 0000000..4a7333e
--- /dev/null
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.security.Principal;
+
+import org.onap.aaf.cadi.UserChain;
+import org.onap.aaf.cadi.principal.TaggedPrincipal;
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_UnAuthPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_UnAuthPrincipal.java
new file mode 100644 (file)
index 0000000..9d694a5
--- /dev/null
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/principal/JU_X509Principal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/principal/JU_X509Principal.java
new file mode 100644 (file)
index 0000000..a45ce24
--- /dev/null
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.principal;
+
+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.aaf.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);
+               // 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);
+               // 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);
+                       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);
+                       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);
+                       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/aaf/cadi/test/taf/JU_NullTafRespTest.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/taf/JU_NullTafRespTest.java
new file mode 100644 (file)
index 0000000..aabed1e
--- /dev/null
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.taf;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.taf.TafResp;
+import org.onap.aaf.cadi.taf.TafResp.RESP;
+
+public class JU_NullTafRespTest {
+
+//     @Before
+//     public void setUp() throws Exception {
+//     }
+
+//     @Test
+//     public void test() throws IOException {
+//             TafResp singleton = NullTafResp.singleton();
+//             TafResp singleton1 = NullTafResp.singleton();
+               
+//             assertEquals(singleton, singleton1);
+               
+//             assertFalse(singleton.isValid());
+               
+//             assertEquals(singleton.isAuthenticated(), RESP.NO_FURTHER_PROCESSING);
+               
+//             assertEquals(singleton.desc(), "All Authentication denied");
+               
+//             assertEquals(singleton.authenticate(), RESP.NO_FURTHER_PROCESSING);
+               
+//             assertNull(singleton.getPrincipal());
+               
+//             assertEquals(singleton.getAccess(), Access.NULL);
+               
+//             assertEquals(singleton.isFailedAttempt(), true);
+//     }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_FQI.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_FQI.java
new file mode 100644 (file)
index 0000000..5717783
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/util/JU_JsonOutputStream.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_JsonOutputStream.java
new file mode 100644 (file)
index 0000000..d07652a
--- /dev/null
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+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.aaf.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/aaf/cadi/test/util/JU_MaskFormatException.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_MaskFormatException.java
new file mode 100644 (file)
index 0000000..6a60df6
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/util/JU_NetMask.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_NetMask.java
new file mode 100644 (file)
index 0000000..be3b1b3
--- /dev/null
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.aaf.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/aaf/cadi/test/util/JU_Split.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_Split.java
new file mode 100644 (file)
index 0000000..e375f6c
--- /dev/null
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.aaf.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/aaf/cadi/test/util/JU_TheConsole.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_TheConsole.java
new file mode 100644 (file)
index 0000000..f168c7f
--- /dev/null
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.aaf.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/aaf/cadi/test/util/JU_UserChainManip.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_UserChainManip.java
new file mode 100644 (file)
index 0000000..d335eda
--- /dev/null
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.UserChain;
+import org.onap.aaf.cadi.util.UserChainManip;
+
+public class JU_UserChainManip {
+
+       @Test
+       public void build(){
+               UserChain.Protocol baseAuth=UserChain.Protocol.BasicAuth;
+               assertEquals(UserChainManip.build(new StringBuilder(""), "app", "id", baseAuth, true).toString(), "app:id:BasicAuth:AS");
+       }
+       
+
+       @Test
+       public void idToNS(){
+               assertEquals(UserChainManip.idToNS(null), "");
+       }
+       
+       @Test
+       public void idToNS1(){
+               assertEquals(UserChainManip.idToNS("t@st"), "st");
+       }
+
+       @Test
+       public void test() {
+               assertEquals("",UserChainManip.idToNS(null));
+               assertEquals("",UserChainManip.idToNS(""));
+               assertEquals("",UserChainManip.idToNS("something"));
+               assertEquals("",UserChainManip.idToNS("something@@"));
+               assertEquals("",UserChainManip.idToNS("something@@."));
+               assertEquals("com",UserChainManip.idToNS("something@com"));
+               assertEquals("com.random",UserChainManip.idToNS("something@random.com"));
+               assertEquals("com.random",UserChainManip.idToNS("@random.com"));
+               assertEquals("com.random",UserChainManip.idToNS("something@random.com."));
+               assertEquals("com.random",UserChainManip.idToNS("something@..random...com..."));
+               assertEquals("com.random.this",UserChainManip.idToNS("something@this.random.com"));
+       }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_Vars.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/util/JU_Vars.java
new file mode 100644 (file)
index 0000000..345c8ca
--- /dev/null
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.onap.aaf.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<String>();
+               list.add("method");
+               assertEquals(Vars.convert(test, list), test);
+       }
+
+       @Test
+       public void convertTest1() {
+               String test = "te%t";
+               List<String> list = new ArrayList<String>();
+               list.add("method");
+               assertEquals(Vars.convert("test", list), "test");
+       }
+
+       @Test
+       public void convertTest2() {
+               String test = "te%s%t";
+               List<String> list = new ArrayList<String>();
+               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/aaf/cadi/test/wsse/JU_WSSE_Read.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/wsse/JU_WSSE_Read.java
new file mode 100644 (file)
index 0000000..26dd43b
--- /dev/null
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.wsse;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+import javax.xml.stream.XMLStreamException;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.BasicCred;
+import org.onap.aaf.cadi.BufferedServletInputStream;
+import org.onap.aaf.cadi.wsse.WSSEParser;
+
+public class JU_WSSE_Read {
+
+//     @Test
+//     public void test() {
+//             try {
+//                     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;
+//                             }
+//                     };
+
+//                     WSSEParser wp = new WSSEParser();
+
+//                     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();
+//                     }
+//             } catch(Exception e) {
+//                     e.printStackTrace(System.err);
+//             }
+//     }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/wsse/JU_XReader.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/wsse/JU_XReader.java
new file mode 100644 (file)
index 0000000..5d765c5
--- /dev/null
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.aaf
+ * * ===========================================================================
+ * * 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.aaf.cadi.test.wsse;
+
+import java.io.FileInputStream;
+
+import javax.xml.stream.events.XMLEvent;
+
+import org.junit.Test;
+import org.onap.aaf.cadi.wsse.XEvent;
+import org.onap.aaf.cadi.wsse.XReader;
+
+public class JU_XReader {
+       //TODO: Gabe [JUnit] Class not found error
+       @Test
+       public void test() throws Exception {
+               FileInputStream fis = new FileInputStream("test/CBUSevent.xml");
+               try {
+                       XReader xr = new XReader(fis);
+                       while(xr.hasNext()) {
+                               XEvent xe = xr.nextEvent();
+                               switch(xe.getEventType()) {
+                                       case XMLEvent.START_DOCUMENT:
+                                               System.out.println("Start Document");
+                                               break;
+                                       case XMLEvent.START_ELEMENT:
+                                               System.out.println("Start Event: " + xe.asStartElement().getName());
+                                               break;
+                                       case XMLEvent.END_ELEMENT:
+                                               System.out.println("End Event: " + xe.asEndElement().getName());
+                                               break;
+                                       case XMLEvent.CHARACTERS:
+                                               System.out.println("Characters: " + xe.asCharacters().getData());
+                                               break;
+                                       case XMLEvent.COMMENT:
+                                               System.out.println("Comment: " + ((XEvent.Comment)xe).value);
+                                               break;
+                               }
+                       }
+               } finally {
+                       fis.close();
+               }
+               
+       }
+
+}
diff --git a/cadi/core/test/AESKeyFile b/cadi/core/test/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/test/CBUSevent.xml b/cadi/core/test/CBUSevent.xml
new file mode 100644 (file)
index 0000000..15fc5f2
--- /dev/null
@@ -0,0 +1,44 @@
+<!--
+  ============LICENSE_START====================================================
+  * org.onap.aaf
+  * ===========================================================================
+  * 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====================================================
+  *
+-->
+<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/test/cadi.properties b/cadi/core/test/cadi.properties
new file mode 100644 (file)
index 0000000..b84509b
--- /dev/null
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START====================================================
+# * org.onap.aaf
+# * ===========================================================================
+# * 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====================================================
+# *
+#-------------------------------------------------------------------------------
+###############################################################################
+# Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+###############################################################################
+# 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
+
+# This is here to force property chaining in tests
+cadi_prop_files=test/cadi.properties.duplicate
diff --git a/cadi/core/test/cadi.properties.duplicate b/cadi/core/test/cadi.properties.duplicate
new file mode 100644 (file)
index 0000000..03c04d0
--- /dev/null
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START====================================================
+# * org.onap.aaf
+# * ===========================================================================
+# * 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====================================================
+# *
+# *
+#-------------------------------------------------------------------------------
+###############################################################################
+# Copyright (c) 2016 AT&T Intellectual Property. All rights reserved.
+###############################################################################
+# 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/test/keyfile b/cadi/core/test/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/oauth-enduser/.gitignore b/cadi/oauth-enduser/.gitignore
new file mode 100644 (file)
index 0000000..ed10e32
--- /dev/null
@@ -0,0 +1,6 @@
+/target/
+/.DS_Store
+/.classpath
+/tokens/
+/bin/
+/.project
diff --git a/cadi/oauth-enduser/.settings/.gitignore b/cadi/oauth-enduser/.settings/.gitignore
new file mode 100644 (file)
index 0000000..e2635f0
--- /dev/null
@@ -0,0 +1,2 @@
+/org.eclipse.jdt.core.prefs
+/org.eclipse.m2e.core.prefs
diff --git a/cadi/oauth-enduser/cadi.properties b/cadi/oauth-enduser/cadi.properties
new file mode 100644 (file)
index 0000000..fb49731
--- /dev/null
@@ -0,0 +1,57 @@
+############################################################
+# Properties for OAuth Example
+#  Jonathan Gathman
+#   on 2018-01-30
+# These properties are the BARE essentials for OAuth calling
+############################################################
+# aaf_locate is the replacement whenever a URL is set to "AAF_LOCATE_URL"
+# at this time, only AAF has this ability.
+#
+# This is, effectively, the Environment you will use for AAF Location
+# TEST ENV
+aaf_locate_url=https://aaftest.test.att.com
+
+# IST ENV
+# aaf_locate_url=https://aafist.test.att.com
+
+# PROD ENV
+# aaf_locate_url=https://aaf.it.att.com
+
+cadi_latitude=<YOUR Latitude (try bing.com/maps)
+cadi_longitude=<YOUR Longitude>
+
+aaf_url=https://AAF_LOCATE_URL/locate/com.att.aaf.service:2.0
+cadi_keyfile=<YOUR Keyfile.  Create with java -jar cadi-core<Version>.jar keygen keyfile.  chmod 400 keyfile>
+
+aaf_id=<YOUR Fully Qualified AAF MechID>
+aaf_password=enc:<YOUR encrypted passwrod.  Create with java -jar cadi-core<Version>.jar digest keyfile>
+# aaf_alias=<YOUR AAF Certman Generated alias FOR the right AAF Env>
+
+# aaf_conn_timeout=6000
+# aaf_timeout=10000
+
+# A Sample AAF OAuth Enabled Service 
+#aaf_oauth2_hello_url=https://AAF_LOCATE_URL/locate/com.att.aaf.hello:2.0/hello
+aaf_oauth2_hello_url=http://135.46.170.156:32245/restservices/echo/v1/testCXF/testGet
+
+# OAuth2
+# AAF OAuth2 Service. 
+aaf_oauth2_token_url=https://AAF_LOCATE_URL/locate/com.att.aaf.token:2.0/token
+aaf_oauth2_introspect_url=https://AAF_LOCATE_URL/locate/com.att.aaf.introspect:2.0/introspect
+
+#ISAM
+aaf_alt_oauth2_domain=isam.att.com
+#aaf_alt_oauth2_client_id=<get from ISAM>
+#aaf_alt_oauth2_domain=csp.att.com
+
+#ISAM TEST
+#aaf_alt_oauth2_token_url=https://oauth.stage.elogin.att.com/mga/sps/oauth/oauth20/token
+#aaf_alt_oauth2_introspect_url=https://oauthapp.stage.att.com/mga/sps/oauth/oauth20/introspect
+#aaf_alt_oauth2_client_secret=enc:<encrypt with cadi tool>
+
+#ISAM PROD
+#aaf_alt_oauth2_token_url=https://oauth.idp.elogin.att.com/mga/sps/oauth/oauth20/token
+#aaf_alt_oauth2_introspect_url=https://oa-app.e-access.att.com/mga/sps/oauth/oauth20/introspect 
+#aaf_alt_oauth2_client_secret=enc:<encrypt with cadi tool>
+
+
diff --git a/cadi/oauth-enduser/pom.xml b/cadi/oauth-enduser/pom.xml
new file mode 100644 (file)
index 0000000..2423c53
--- /dev/null
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============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====================================================
+ *
+-->
+<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>
+       <name>AAF CADI Sample OAuth EndUser</name>
+       <groupId>org.onap.aaf.cadi</groupId>
+       <version>1.5.0-SNAPSHOT</version>
+       <artifactId>aaf-cadi-oauth-enduser</artifactId>
+
+       <packaging>jar</packaging>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+       
+       <dependencies>
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-core</artifactId>
+                       <version>1.5.0-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-aaf</artifactId>
+                       <version>1.5.0-SNAPSHOT</version>
+               </dependency>
+       </dependencies>
+
+       <build>
+               <pluginManagement>
+                       <plugins>
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-compiler-plugin</artifactId>
+                                       <version>2.3.2</version>
+                                       <configuration>
+                                               <source>1.8</source>
+                                               <target>1.8</target>
+                                       </configuration>
+                               </plugin>
+
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <version>2.4</version>
+                                       <artifactId>maven-jar-plugin</artifactId>
+                                       <configuration>
+                                               <outputDirectory>target</outputDirectory>
+                                       </configuration>
+                               </plugin>
+                               <plugin>
+                                       <artifactId>maven-assembly-plugin</artifactId>
+                                       <version>2.4</version>
+                                       <configuration>
+                                               <archive>
+                                                       <manifest>
+                                                               <mainClass>org.onap.aaf.cadi.enduser.OAuthExample</mainClass>
+                                                       </manifest>
+                                               </archive>
+                                               <descriptors>
+                                                       <descriptor>src/main/assemble/cadi-oauth-enduser-assemble.xml</descriptor>
+                                               </descriptors>
+                                       </configuration>
+                               </plugin>
+                       </plugins>
+               </pluginManagement>
+       </build>
+
+</project>
diff --git a/cadi/oauth-enduser/src/.gitignore b/cadi/oauth-enduser/src/.gitignore
new file mode 100644 (file)
index 0000000..9bb88d3
--- /dev/null
@@ -0,0 +1 @@
+/.DS_Store
diff --git a/cadi/oauth-enduser/src/main/java/com/att/cadi/enduser/OAuthExample.java b/cadi/oauth-enduser/src/main/java/com/att/cadi/enduser/OAuthExample.java
new file mode 100644 (file)
index 0000000..9cb4b4a
--- /dev/null
@@ -0,0 +1,233 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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 com.att.cadi.enduser;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.security.GeneralSecurityException;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.client.Future;
+import org.onap.aaf.cadi.client.Rcli;
+import org.onap.aaf.cadi.client.Result;
+import org.onap.aaf.cadi.client.Retryable;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.oauth.TimedToken;
+import org.onap.aaf.cadi.oauth.TokenClient;
+import org.onap.aaf.cadi.oauth.TokenClientFactory;
+import org.onap.aaf.cadi.oauth.TzClient;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+
+import aafoauth.v2_0.Introspect;
+import aafoauth.v2_0.Token;
+
+
+public class OAuthExample {
+       private static TokenClientFactory tcf;
+       private static PropAccess access;
+
+       public final static void main(final String args[]) {
+               // These Objects are expected to be Long-Lived... Construct once
+               
+               // Property Access
+                       // This method will allow you to set "cadi_prop_files" (or any other property) on Command line 
+               access = new PropAccess(args);
+               
+                       // access = PropAccess();
+                       // Note: This style will load "cadi_prop_files" from VM Args
+               
+               // Token aware Client Factory
+               try {
+                       tcf = TokenClientFactory.instance(access);
+               } catch (APIException | GeneralSecurityException | IOException | CadiException e1) {
+                       access.log(e1, "Unable to setup OAuth Client Factory, Fail Fast");
+                       System.exit(1);
+               }
+               
+               
+               // Obtain Endpoints for OAuth2 from Properties.  Expected is "cadi.properties" file, pointed to by "cadi_prop_files"
+               String tokenServiceURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL);
+               String tokenIntrospectURL = access.getProperty(Config.AAF_OAUTH2_INTROSPECT_URL);
+
+               
+               // Get Properties
+               final String endServicesURL = access.getProperty(Config.AAF_OAUTH2_HELLO_URL);
+
+               final int CALL_TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CALL_TIMEOUT,Config.AAF_CALL_TIMEOUT_DEF));
+               
+               try {
+                       //////////////////////////////////////////////////////////////////////
+                       // Scenario 1:
+                       // Get and use an OAuth Client, which understands Token Management
+                       //////////////////////////////////////////////////////////////////////
+                       // Create a Token Client, that gets its tokens from expected OAuth Server
+                       //   In this example, it is AAF, but it can be the Alternate OAuth
+
+                       TokenClient tc = tcf.newClient(tokenServiceURL); // can set your own timeout here (url, timeoutMilliseconds)
+                       // Set your Application (MicroService, whatever) Credentials here
+                       //   These are how your Application is known, particularly to the OAuth Server. 
+                       //   If AAF Token server, then its just the same as your other AAF MechID creds
+                       //   If it is the Alternate OAUTH, you'll need THOSE credentials.  See that tool's Onboarding procedures.
+                       String client_id = access.getProperty(Config.AAF_APPID);
+                       String client_secret = access.getProperty(Config.AAF_APPPASS);
+                       tc.client_creds(client_id, client_secret);
+                       
+                       // If you are working with Credentials the End User, set username/password as appropriate to the OAuth Server
+                       // tc.password(end_user_id, end_user_password);
+                       // IMPORTANT:
+                       //   if you are setting client Credentials, you MAY NOT reuse this Client mid-transaction.  You CAN reuse after setting
+                       //  tc.clearEndUser();
+                       // You may want to see "Pooled Client" example, using special CADI utility
+
+                       // With AAF, the Scopes you put in are the AAF Namespaces you want access to.  Your Token will contain the
+                       // AAF Permissions of the Namespaces (you can put in more than one), the user name (or client_id if no user_name),
+                       // is allowed to see.
+                       
+                       // Here's a trick to get the namespace out of a Fully Qualified AAF Identity (your MechID)
+                       String ns = FQI.reverseDomain(client_id);
+                       System.out.printf("\nNote: The AAF Namespace of FQI (Fully Qualified Identity) %s is %s\n\n",client_id, ns);
+
+                       // Now, we can get a Token.  Note: for "scope", use AAF Namespaces to get AAF Permissions embedded in
+                       // Note: getToken checks if Token is expired, if so, then refreshes before handing back.
+                       Result<TimedToken> rtt = tc.getToken(ns,"org.onap.test");
+                       
+                       // Note: you can clear a Token's Disk/Memory presence by
+                       //  1) removing the Token from the "token/outgoing" directory on the O/S
+                       //  2) programmatically by calling "clearToken" with exact params as "getToken", when it has the same credentials set
+                       //       tc.clearToken("org.onap.aaf","org.onap.test");
+                       
+                       // Result Object can be queried for success
+                       if(rtt.isOK()) {
+                               TimedToken token = rtt.value;
+                               print(token); // Take a look at what's in a Token
+                               
+                               // Use this Token in your client calls with "Tokenized Client" (TzClient)
+                               // These should NOT be used cross thread.
+                               TzClient helloClient = tcf.newTzClient(endServicesURL);
+                               helloClient.setToken(client_id, token);
+                               
+                               // This client call style, "best" call with "Retryable" inner class covers finding an available Service 
+                               // (when Multi-services exist) for the best service, based (currently) on distance.
+                               //
+                               // the "Generic" in Type gives a Return Value for the Code, which you can set on the "best" method
+                               // Note that variables used in the inner class from this part of the code must be "final", see "CALL_TIMEOUT"
+                               String rv = helloClient.best(new Retryable<String>() {
+                                       @Override
+                                       public String code(Rcli<?> client) throws CadiException, ConnectException, APIException {
+                                               Future<String> future = client.read(null,"text/plain");
+                                               // The "future" calling method allows you to do other processing, such as call more than one backend
+                                               // client before picking up the result
+                                               // If "get" matches the HTTP Code for the method (i.e. read HTTP Return value is 200), then 
+                                               if(future.get(CALL_TIMEOUT)) {
+                                                       // Client Returned expected value
+                                                       return future.value;
+                                               } else {
+                                                       throw new APIException(future.code()  + future.body());
+                                               }                                       
+                                       }
+                               });
+                               
+                               // You want to do something with returned value.  Here, we say "hello"
+                               System.out.printf("\nPositive Response from Hello: %s\n",rv);
+                               
+                               
+                               //////////////////////////////////////////////////////////////////////
+                               // Scenario 2:
+                               // As a Service, read Introspection information as proof of Authenticated Authorization
+                               //////////////////////////////////////////////////////////////////////
+                               // CADI Framework (i.e. CadiFilter) works with the Introspection to drive the J2EE interfaces (
+                               // i.e. if(isUserInRole("ns.perm|instance|action")) {...
+                               //
+                               // Here, however, is a way to introspect via Java
+                               //
+                               // now, call Introspect (making sure right URLs are set in properties)
+                               // We need a Different Introspect TokenClient, because different Endpoint (and usually different Services)
+                               TokenClient tci = tcf.newClient(tokenIntrospectURL);
+                               tci.client_creds(client_id, client_secret);
+                               Result<Introspect> is = tci.introspect(token.getAccessToken());
+                               if(is.isOK()) {
+                                       // Note that AAF will add JSON set of Permissions as part of "Content:", legitimate extension of OAuth Structure
+                                       print(is.value); // do something with Introspect Object
+                               } else {
+                                       access.printf(Level.ERROR, "Unable to introspect OAuth Token %s: %d %s\n",
+                                                       token.getAccessToken(),rtt.code,rtt.error);
+                               }
+                       } else {
+                               access.printf(Level.ERROR, "Unable to obtain OAuth Token: %d %s\n",rtt.code,rtt.error);
+                       }
+                       
+               } catch (CadiException | LocatorException | APIException | IOException e) {
+                       e.printStackTrace();
+               }
+       }
+       
+       /////////////////////////////////////////////////////////////
+       // Examples of Object Access
+       /////////////////////////////////////////////////////////////
+       private static void print(Token t) {
+               GregorianCalendar exp_date = new GregorianCalendar();
+               exp_date.add(GregorianCalendar.SECOND, t.getExpiresIn());
+               System.out.printf("Access Token\n\tToken:\t\t%s\n\tToken Type:\t%s\n\tExpires In:\t%d (%s)\n\tScope:\t\t%s\n\tRefresh Token:\t%s\n",
+               t.getAccessToken(),
+               t.getTokenType(),
+               t.getExpiresIn(),
+               Chrono.timeStamp(new Date(System.currentTimeMillis()+(t.getExpiresIn()*1000))),
+               t.getScope(),
+               t.getRefreshToken());
+       }
+       
+       private static void print(Introspect ti) {
+               if(ti==null || ti.getClientId()==null) {
+                       System.out.println("Empty Introspect");
+                       return;
+               }
+               Date exp = new Date(ti.getExp()*1000); // seconds
+               System.out.printf("Introspect\n"
+                               + "\tAccessToken:\t%s\n"
+                               + "\tClient-id:\t%s\n"
+                               + "\tClient Type:\t%s\n"
+                               + "\tActive:  \t%s\n"
+                               + "\tUserName:\t%s\n"
+                               + "\tExpires: \t%d (%s)\n"
+                               + "\tScope:\t\t%s\n"
+                               + "\tContent:\t\t%s\n",
+               ti.getAccessToken(),
+               ti.getClientId(),
+               ti.getClientType(),
+               ti.isActive()?Boolean.TRUE.toString():Boolean.FALSE.toString(),
+               ti.getUsername(),
+               ti.getExp(),
+               Chrono.timeStamp(exp),
+               ti.getScope(),
+               ti.getContent()==null?"":ti.getContent());
+               
+               System.out.println();
+       }
+
+}
diff --git a/cadi/pom.xml b/cadi/pom.xml
new file mode 100644 (file)
index 0000000..7fecc03
--- /dev/null
@@ -0,0 +1,383 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============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====================================================
+ *
+-->
+<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>
+       <groupId>org.onap.aaf.cadi</groupId>
+       <artifactId>parent</artifactId>
+       <name>AAF CADI Parent (Code, Access, Data, Identity)</name>
+       <version>1.5.0-SNAPSHOT</version>
+       <inceptionYear>2015-07-20</inceptionYear>
+       <organization>
+               <name>ONAP</name>
+       </organization>
+       <packaging>pom</packaging>
+
+       <properties>
+               <skipSigning>true</skipSigning>
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+               <project.miscVersion>1.3.0-SNAPSHOT</project.miscVersion>
+               <project.authClientVersion>2.10-SNAPSHOT</project.authClientVersion>
+               <project.jettyVersion>9.3.9.v20160517</project.jettyVersion>
+               <powermock.version>1.5.1</powermock.version>
+       </properties>
+
+       <!-- ============================================================== -->
+       <!-- Define the major contributors and developers of CADI -->
+       <!-- ============================================================== -->
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-all</artifactId>
+                       <version>1.9.5</version>
+                       <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>
+                       <version>4.10</version>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+
+       <!-- ============================================================== -->
+       <!-- Define sub-projects (modules) -->
+       <!-- ============================================================== -->
+       <modules>
+               <module>core</module>
+               <module>client</module>
+               <module>aaf</module>
+               <module>cass</module>
+               
+               <module>oauth-enduser</module>
+               <module>shiro</module>
+       </modules>
+
+       <!-- ============================================================== -->
+       <!-- Define project-wide dependencies -->
+       <!-- ============================================================== -->
+       <dependencyManagement>
+               <dependencies>
+                       <dependency>
+                               <groupId>org.onap.aaf.auth</groupId>
+                               <artifactId>aaf-auth-client</artifactId>
+                               <version>${project.authClientVersion}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-core</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-oauth</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+
+                       <!-- Prevent Cycles in Testing  -->
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-core</artifactId>
+                               <version>${project.version}</version>
+                               <classifier>tests</classifier>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-jetty</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-cass</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-aaf</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-aaf</artifactId>
+                               <version>${project.version}</version>
+                               <classifier>full</classifier>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.cadi</groupId>
+                               <artifactId>aaf-cadi-client</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.misc</groupId>
+                               <artifactId>aaf-misc-env</artifactId>
+                               <version>${project.miscVersion}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.misc</groupId>
+                               <artifactId>aaf-misc-rosetta</artifactId>
+                               <version>${project.miscVersion}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.onap.aaf.misc</groupId>
+                               <artifactId>aaf-misc-log4j</artifactId>
+                               <version>${project.miscVersion}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-servlet</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+                       
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-io</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+       
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-security</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+       
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-http</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+       
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-util</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+       
+                       <dependency>
+                               <groupId>org.eclipse.jetty</groupId>
+                               <artifactId>jetty-server</artifactId>
+                               <version>${project.jettyVersion}</version>
+                       </dependency>
+
+                       <dependency>
+                           <groupId>javax.servlet</groupId>
+                           <artifactId>javax.servlet-api</artifactId>
+                               <version>3.0.1</version>
+                       </dependency>
+                       
+                       <dependency>
+                               <groupId>org.slf4j</groupId>
+                               <artifactId>slf4j-api</artifactId>
+                               <version>1.7.5</version>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+
+       <!-- ============================================================== -->
+       <!-- Define common plugins and make them available for all modules -->
+       <!-- ============================================================== -->
+       <build>
+               <testSourceDirectory>src/test/java</testSourceDirectory>
+               <plugins>
+               </plugins>
+               <pluginManagement>                      
+                       <plugins>
+                               <plugin>
+                                       <inherited>true</inherited>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-compiler-plugin</artifactId>
+                                       <version>2.3.2</version>
+                                       <configuration>
+                                               <source>1.7</source>
+                                               <target>1.7</target>
+                                       </configuration>
+                               </plugin>
+       
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <version>2.4</version>
+                                       <artifactId>maven-jar-plugin</artifactId>
+                                       <configuration>
+                                               <outputDirectory>target</outputDirectory>
+                                               <archive>
+                                                       <manifestEntries>
+                                                               <Sealed>true</Sealed>
+                                                       </manifestEntries>
+                                               </archive>
+                                       </configuration>
+                               </plugin>
+
+                               <!-- Define the javadoc plugin -->
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-javadoc-plugin</artifactId>
+                                       <version>2.10</version>
+                                       <configuration>
+                                               <excludePackageNames>org.opendaylight.*</excludePackageNames>
+                                       </configuration>
+                               </plugin>
+       
+                               <plugin>
+                                       <artifactId>maven-release-plugin</artifactId>
+                                       <version>2.5.2</version>
+                                       <configuration>
+                                               <goals>-s ${mvn.settings} deploy</goals>
+                                       </configuration>
+                               </plugin>
+       
+                               <plugin>
+                                       <artifactId>maven-assembly-plugin</artifactId>
+                                       <version>2.5.5</version>
+                               </plugin>
+       
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-deploy-plugin</artifactId>
+                                       <version>2.8.1</version>
+                                       <configuration>
+                                               <skip>false</skip>
+                                       </configuration>
+       
+                               </plugin>
+       
+                               <plugin>
+                                       <groupId>org.apache.maven.plugins</groupId>
+                                       <artifactId>maven-dependency-plugin</artifactId>
+                                       <version>2.10</version>
+                               </plugin>
+       
+                               <!-- Maven surefire plugin for testing -->
+                               <plugin>
+                                       <artifactId>maven-surefire-plugin</artifactId>
+                                       <version>2.17</version>
+                                       <configuration>
+                                               <skipTests>true</skipTests>
+                                       </configuration>
+                               </plugin>
+                               
+                               <!--This plugin's configuration is used to store Eclipse m2e settings 
+                                       only. It has no influence on the Maven build itself. -->
+                               <plugin>
+                                       <groupId>org.eclipse.m2e</groupId>
+                                       <artifactId>lifecycle-mapping</artifactId>
+                                       <version>1.0.0</version>
+                                       <configuration>
+                                               <lifecycleMappingMetadata>
+                                                       <pluginExecutions>
+                                                               <pluginExecution>
+                                                                       <pluginExecutionFilter>
+                                                                               <groupId>
+                                                                                       org.codehaus.mojo
+                                                                               </groupId>
+                                                                               <artifactId>
+                                                                                       jaxb2-maven-plugin
+                                                                               </artifactId>
+                                                                               <versionRange>
+                                                                                       [1.3,)
+                                                                               </versionRange>
+                                                                               <goals>
+                                                                                       <goal>xjc</goal>
+                                                                               </goals>
+                                                                       </pluginExecutionFilter>
+                                                                       <action>
+                                                                               <ignore />
+                                                                       </action>
+                                                               </pluginExecution>
+                                                       </pluginExecutions>
+                                               </lifecycleMappingMetadata>
+                                       </configuration>
+                               </plugin>
+                               
+                       </plugins>
+               </pluginManagement>                     
+       </build>
+
+
+       <!-- ============================================================== -->
+       <!-- Maven Central Repository Information -->
+       <!-- ============================================================== -->
+       <distributionManagement>
+               <repository>
+                       <id>nexus</id>
+                       <name>attarch-releases</name>
+                       <url>http://mavencentral.it.att.com:8084/nexus/content/repositories/attarch-releases</url>
+               </repository>
+               <snapshotRepository>
+                       <id>nexus</id>
+                       <name>attarch-snapshots</name>
+                       <url>http://mavencentral.it.att.com:8084/nexus/content/repositories/attarch-snapshots</url>
+               </snapshotRepository>
+       </distributionManagement>
+</project>
diff --git a/cadi/shiro/.gitignore b/cadi/shiro/.gitignore
new file mode 100644 (file)
index 0000000..cb0cea3
--- /dev/null
@@ -0,0 +1,5 @@
+/bin/
+/target/
+/.project
+/.classpath
+/.settings
diff --git a/cadi/shiro/pom.xml b/cadi/shiro/pom.xml
new file mode 100644 (file)
index 0000000..07ff5ab
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============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====================================================
+ *
+-->
+<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.aaf.cadi</groupId>
+               <artifactId>parent</artifactId>
+               <version>1.5.0-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+
+       <modelVersion>4.0.0</modelVersion>
+       <name>AAF CADI Shiro Plugin</name>
+       <packaging>jar</packaging>
+       <artifactId>aaf-cadi-shiro</artifactId>
+
+       <developers>
+               <developer>
+                       <name>Jonathan Gathman</name>
+                       <email>jonathan.gathman@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Architect</role>
+                               <role>Lead Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Gabe Maurer</name>
+                       <email>gabe.maurer@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+               <developer>
+                       <name>Ian Howell</name>
+                       <email>ian.howell@att.com</email>
+                       <organization>ATT</organization>
+                       <roles>
+                               <role>Developer</role>
+                       </roles>
+               </developer>
+       </developers>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onap.aaf.cadi</groupId>
+                       <artifactId>aaf-cadi-aaf</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.shiro</groupId>
+                       <artifactId>shiro-core</artifactId>
+                       <version>1.3.2</version>
+               </dependency>
+       </dependencies>
+       <build>
+               <plugins />
+       </build>
+</project>
diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java
new file mode 100644 (file)
index 0000000..a1d304b
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro;
+
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.Access.Level;
+
+public class AAFAuthenticationInfo implements AuthenticationInfo {
+       private static final long serialVersionUID = -1502704556864321020L;
+       // We assume that Shiro is doing Memory Only, and this salt is not needed cross process
+       private final static int salt = new SecureRandom().nextInt(); 
+
+       private final AAFPrincipalCollection apc;
+       private final byte[] hash;
+       private Access access;
+
+       public AAFAuthenticationInfo(Access access, String username, String password) {
+               this.access = access;
+               apc = new AAFPrincipalCollection(username);
+               hash = getSaltedCred(password);
+       }
+       @Override
+       public byte[] getCredentials() {
+               access.log(Level.DEBUG, "AAFAuthenticationInfo.getCredentials");
+               return hash;
+       }
+
+       @Override
+       public PrincipalCollection getPrincipals() {
+               access.log(Level.DEBUG, "AAFAuthenticationInfo.getPrincipals");
+               return apc;
+       }
+
+       public boolean matches(AuthenticationToken atoken) {
+               if(atoken instanceof UsernamePasswordToken) {
+                       UsernamePasswordToken upt = (UsernamePasswordToken)atoken;
+                       if(apc.getPrimaryPrincipal().getName().equals(upt.getPrincipal())) {
+                               byte[] newhash = getSaltedCred(new String(upt.getPassword()));
+                               if(newhash.length==hash.length) {
+                                       for(int i=0;i<hash.length;++i) {
+                                               if(hash[i]!=newhash[i]) {
+                                                       return false;
+                                               }
+                                       }
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+       
+       private byte[] getSaltedCred(String password) {
+               byte[] pbytes = password.getBytes();
+               ByteBuffer bb = ByteBuffer.allocate(pbytes.length+Integer.SIZE/8);
+               bb.asIntBuffer().put(salt);
+               bb.put(password.getBytes());
+               try {
+                       return Hash.hashSHA256(bb.array());
+               } catch (NoSuchAlgorithmException e) {
+                       return new byte[0]; // should never get here
+               }
+       }
+}
diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java
new file mode 100644 (file)
index 0000000..9093590
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.Permission;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+
+/**
+ * We treate "roles" and "permissions" in a similar way for first pass.
+ * 
+ * @author jg1555
+ *
+ */
+public class AAFAuthorizationInfo implements AuthorizationInfo {
+       private static final long serialVersionUID = -4805388954462426018L;
+       private Access access;
+       private Principal bait;
+       private List<org.onap.aaf.cadi.Permission> pond;
+       private ArrayList<String> sPerms;
+       private ArrayList<Permission> oPerms;
+
+       public AAFAuthorizationInfo(Access access, Principal bait, List<org.onap.aaf.cadi.Permission> pond) {
+               this.access = access;
+               this.bait = bait;
+               this.pond = pond;
+               sPerms=null;
+               oPerms=null;
+       }
+       
+       public Principal principal() {
+               return bait;
+       }
+       
+       @Override
+       public Collection<Permission> getObjectPermissions() {
+               access.log(Level.DEBUG, "AAFAuthorizationInfo.getObjectPermissions");
+               synchronized(bait) {
+                       if(oPerms == null) {
+                               oPerms = new ArrayList<Permission>(); 
+                               for(final org.onap.aaf.cadi.Permission p : pond) {
+                                       oPerms.add(new AAFShiroPermission(p));
+                               }
+                       }
+               }
+               return oPerms;
+       }
+
+       @Override
+       public Collection<String> getRoles() {
+               access.log(Level.DEBUG, "AAFAuthorizationInfo.getRoles");
+               // Until we decide to make Roles available, tie into String based permissions.
+               return getStringPermissions();
+       }
+
+       @Override
+       public Collection<String> getStringPermissions() {
+               access.log(Level.DEBUG, "AAFAuthorizationInfo.getStringPermissions");
+               synchronized(bait) {
+                       if(sPerms == null) {
+                               sPerms = new ArrayList<String>(); 
+                               for(org.onap.aaf.cadi.Permission p : pond) {
+                                       sPerms.add(p.getKey());
+                               }
+                       }
+               }
+               return sPerms;
+       }
+
+}
diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java
new file mode 100644 (file)
index 0000000..145968d
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.shiro.subject.PrincipalCollection;
+
+public class AAFPrincipalCollection implements PrincipalCollection {
+       private static final long serialVersionUID = 558246013419818831L;
+       private static final Set<String> realmSet;
+       private final Principal principal;
+       private List<Principal> list=null;
+       private Set<Principal> set=null;
+
+       static {
+               realmSet = new HashSet<String>();
+               realmSet.add(AAFRealm.AAF_REALM);
+       }
+       
+       public AAFPrincipalCollection(Principal p) {
+               principal = p;
+       }
+
+       public AAFPrincipalCollection(final String principalName) {
+               principal =     new Principal() {
+                       private final String name = principalName;
+                       @Override
+                       public String getName() {
+                               return name;
+                       }
+               };
+       }
+
+       @Override
+       public Iterator<Principal> iterator() {
+               return null;
+       }
+
+       @Override
+       public List<Principal> asList() {
+               if(list==null) {
+                       list = new ArrayList<Principal>();
+               }
+               list.add(principal);
+               return list;
+       }
+
+       @Override
+       public Set<Principal> asSet() {
+               if(set==null) {
+                       set = new HashSet<Principal>();
+               }
+               set.add(principal);
+               return set;
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T> Collection<T> byType(Class<T> cls) {
+               Collection<T> coll = new ArrayList<T>();
+               if(cls.isAssignableFrom(Principal.class)) {
+                       coll.add((T)principal);
+               }
+               return coll;
+       }
+
+       @Override
+       public Collection<Principal> fromRealm(String realm) {
+               if(AAFRealm.AAF_REALM.equals(realm)) {
+                       return asList();
+               } else {
+                       return new ArrayList<Principal>();
+               }
+       }
+
+       @Override
+       public Principal getPrimaryPrincipal() {
+               return principal;
+       }
+
+       @Override
+       public Set<String> getRealmNames() {
+               return realmSet;
+       }
+
+       @Override
+       public boolean isEmpty() {
+               return principal==null;
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T> T oneByType(Class<T> cls) {
+               if(cls.isAssignableFrom(Principal.class)) {
+                       return (T)principal;
+               }
+               return null;
+       }
+
+}
diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java
new file mode 100644 (file)
index 0000000..006547a
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.misc.env.APIException;
+
+public class AAFRealm extends AuthorizingRealm {
+       public static final String AAF_REALM = "AAFRealm";
+       
+       private PropAccess access;
+       private AAFCon<?> acon;
+       private AAFAuthn<?> authn;
+       private HashSet<Class<? extends AuthenticationToken>> supports;
+       private AAFLurPerm authz;
+       
+
+       /**
+        * 
+        * There appears to be no configuration objects or references available for CADI to start with.
+        *  
+        */
+       public AAFRealm () {
+               access = new PropAccess(); // pick up cadi_prop_files from VM_Args
+               String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES);
+               if(cadi_prop_files==null) {
+                       String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm.";
+                       access.log(Level.INIT,msg);
+                       throw new RuntimeException(msg);
+               } else {
+                       try {
+                               acon = AAFCon.newInstance(access);
+                               authn = acon.newAuthn();
+                               authz = acon.newLur(authn);
+                       } catch (APIException | CadiException | LocatorException e) {
+                               String msg = "Cannot initiate AAFRealm";
+                               access.log(Level.INIT,msg,e.getMessage());
+                               throw new RuntimeException(msg,e);
+                       }
+               }
+               supports = new HashSet<Class<? extends AuthenticationToken>>();
+               supports.add(UsernamePasswordToken.class);
+       }
+
+       @Override
+       protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+               access.log(Level.DEBUG, "AAFRealm.doGetAuthenticationInfo",token);
+               
+               final UsernamePasswordToken upt = (UsernamePasswordToken)token;
+               String password=new String(upt.getPassword());
+               String err;
+               try {
+                       err = authn.validate(upt.getUsername(),password);
+               } catch (IOException|CadiException e) {
+                       err = "Credential cannot be validated";
+                       access.log(e, err);
+               }
+               
+               if(err != null) {
+                       access.log(Level.DEBUG, err);
+                       throw new AuthenticationException(err);
+               }
+
+           return new AAFAuthenticationInfo(
+                       access,
+                       upt.getUsername(),
+                       password
+           );
+       }
+
+       @Override
+       protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException {
+               if(ai instanceof AAFAuthenticationInfo) {
+                       if(!((AAFAuthenticationInfo)ai).matches(atoken)) {
+                               throw new AuthenticationException("Credentials do not match");
+                       }
+               } else {
+                       throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo");
+               }
+       }
+
+
+       @Override
+       protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+               access.log(Level.DEBUG, "AAFRealm.doGetAuthenthorizationInfo");
+               Principal bait = (Principal)principals.getPrimaryPrincipal();
+               List<Permission> pond = new ArrayList<Permission>();
+               authz.fishAll(bait,pond);
+               
+               return new AAFAuthorizationInfo(access,bait,pond);
+       
+       }
+
+       @Override
+       public boolean supports(AuthenticationToken token) {
+               return supports.contains(token.getClass());
+       }
+
+       @Override
+       public String getName() {
+               return AAF_REALM;
+       }
+
+}
diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java
new file mode 100644 (file)
index 0000000..a348a04
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro;
+
+import org.apache.shiro.authz.Permission;
+
+public class AAFShiroPermission implements Permission {
+       private org.onap.aaf.cadi.Permission perm;
+       public AAFShiroPermission(org.onap.aaf.cadi.Permission perm) {
+               this.perm = perm;
+       }
+       @Override
+       public boolean implies(Permission sp) {
+               if(sp instanceof AAFShiroPermission) {
+                       if(perm.match(((AAFShiroPermission)sp).perm)){
+                               return true;
+                       }
+               }
+               return false;
+       }
+       
+       @Override
+       public String toString() {
+               return perm.toString();
+       }
+
+}
diff --git a/cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java b/cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java
new file mode 100644 (file)
index 0000000..156e54b
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * 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.aaf.cadi.shiro.test;
+
+import java.util.ArrayList;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.junit.Test;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.shiro.AAFRealm;
+import org.onap.aaf.cadi.shiro.AAFShiroPermission;
+
+import junit.framework.Assert;
+
+public class JU_AAFRealm {
+
+       @Test
+       public void test() {
+               // NOTE This is a live test.  This JUnit needs to be built with "Mock"
+               try {
+                       System.setProperty(Config.CADI_PROP_FILES, "/opt/app/osaaf/etc/org.osaaf.common.props");
+                       TestAAFRealm ar = new TestAAFRealm();
+                       
+                       UsernamePasswordToken upt = new UsernamePasswordToken("jonathan@people.osaaf.org", "new2You!");
+                       AuthenticationInfo ani = ar.authn(upt);
+                       
+                       AuthorizationInfo azi = ar.authz(ani.getPrincipals());
+                       // Change this to something YOU have, Sai...
+                       
+                       testAPerm(true,azi,"org.access","something","*");
+                       testAPerm(false,azi,"org.accessX","something","*");
+               } catch (Throwable t) {
+                       t.printStackTrace();
+                       Assert.fail();
+               }
+       }
+
+       private void testAPerm(boolean expect,AuthorizationInfo azi, String type, String instance, String action) {
+               
+               AAFShiroPermission testPerm = new AAFShiroPermission(new AAFPermission(type,instance,action,new ArrayList<String>()));
+
+               boolean any = false;
+               for(Permission p : azi.getObjectPermissions()) {
+                       if(p.implies(testPerm)) {
+                               any = true;
+                       }
+               }
+               if(expect) {
+                       Assert.assertTrue(any);
+               } else {
+                       Assert.assertFalse(any);
+               }
+
+               
+       }
+
+       /**
+        * Note, have to create a derived class, because "doGet"... are protected
+        */
+       private class TestAAFRealm extends AAFRealm {
+               public AuthenticationInfo authn(UsernamePasswordToken upt) {
+                       return doGetAuthenticationInfo(upt);
+               }
+               public AuthorizationInfo authz(PrincipalCollection pc) {
+                       return doGetAuthorizationInfo(pc);
+               }
+               
+       }
+}