diff --git a/tck/common/pom.xml b/tck/common/pom.xml index fb55045..3791f50 100644 --- a/tck/common/pom.xml +++ b/tck/common/pom.xml @@ -1,6 +1,6 @@ - profile-spi + spi - 11 + 17 UTF-8 @@ -134,8 +134,8 @@ org.omnifaces.arquillian - glassfish-client-ee9 - 1.0 + glassfish-client-ee10 + 1.4 test @@ -172,21 +172,21 @@ jakarta.enterprise jakarta.enterprise.cdi-api - 4.0.0 + 4.1.0 provided jakarta.validation jakarta.validation-api - 3.0.1 + 3.1.0 provided jakarta.ejb jakarta.ejb-api - 4.0.0 + 4.0.1 provided true @@ -194,7 +194,7 @@ jakarta.annotation jakarta.annotation-api - 2.1.0 + 3.0.0 provided @@ -203,7 +203,6 @@ junit junit - 4.13.2 test @@ -236,7 +235,7 @@ org.htmlunit htmlunit - 3.11.0 + 4.0.0 test @@ -267,7 +266,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 org.apache.maven.plugins @@ -277,12 +276,12 @@ org.apache.maven.plugins maven-dependency-plugin - 3.2.0 + 3.6.1 org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M5 + 3.2.5 org.apache.maven.plugins @@ -295,7 +294,6 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0 enforce-maven @@ -305,7 +303,7 @@ - 3.6.3 + 3.8.6 @@ -327,12 +325,12 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.1 org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.2.5 false @@ -384,7 +382,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 3.0.0-M6 + 3.2.5 true true @@ -423,6 +421,14 @@ 1.4 test + + + + org.glassfish.epicyro + epicyro + 3.1.0-SNAPSHOT + test + @@ -753,21 +759,5 @@ true - - - old-tck - - - true - - - - - old-tck - - diff --git a/tck/profile-spi/pom.xml b/tck/profile-spi/pom.xml index a96b45b..1b6ce5c 100644 --- a/tck/profile-spi/pom.xml +++ b/tck/profile-spi/pom.xml @@ -33,6 +33,11 @@ + + jakarta.enterprise.concurrent + jakarta.enterprise.concurrent-api + 3.1.0 + org.jakartaee jaspic-common @@ -43,6 +48,16 @@ jakarta.jws-api 3.0.0 + + org.glassfish.metro + webservices-rt + 4.0.3 + + + org.glassfish.main.security + webservices.security + 8.0.0-SNAPSHOT + jakarta.xml.ws jakarta.xml.ws-api @@ -130,7 +145,7 @@ ${basedir}/src/main/webapp/WEB-INF/HelloService.wsdl - http://localhost:8080/jaxws-endpoint/EBookStoreImplService?wsdl + http://localhost:8080/spitests_servlet_web/HelloService?wsdl true ${basedir}/src/main/java diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java index 0ca115c..21ce839 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java @@ -370,7 +370,7 @@ private boolean GroupPrincipalCallbackSupport() { // however, for the case of mandatory authen, we will want // to create a CPC using a username as opposed to a null principal. - Subject subject = clientSubject; // new Subject(); + Subject subject = clientSubject != null? clientSubject : new Subject(); String strArray[] = { "Administrator" }; GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(subject, strArray); diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java new file mode 100644 index 0000000..ce2c7e8 --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.MessageInfo; +import jakarta.xml.soap.MimeHeaders; +import jakarta.xml.soap.Name; +import jakarta.xml.soap.Node; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPElement; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Iterator; +import java.util.Map; +import javax.security.auth.callback.CallbackHandler; + +public class SOAPTSServerAuthConfig extends TSServerAuthConfig { + + protected SOAPTSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props) { + super(layer, applicationCtxt, cbkHandler, props); + } + + public SOAPTSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props, TSLogger tsLogger) { + super(layer, applicationCtxt, cbkHandler, props); + + if (tsLogger != null) { + logger = tsLogger; + } + + String str = "TSServerAuthConfig called for layer=" + layer + " : appContext=" + applicationCtxt; + logger.log(INFO, str); + } + + @Override + public String getAuthContextID(MessageInfo messageInfo) { + logger.log(INFO, "getAuthContextID called"); + String authContextID = null; + + if (messageLayer.equals(LAYER_SOAP)) { + authContextID = getOpName((SOAPMessage) messageInfo.getRequestMessage()); + + logger.log(INFO, "getAuthContextID() called for layer=" + messageLayer + " shows AuthContextId=" + authContextID); + + } else if (messageLayer.equals(LAYER_SERVLET)) { + super.getAuthContextID(messageInfo); + } + + return authContextID; + } + + private String getOpName(SOAPMessage message) { + if (message == null) { + return null; + } + + String opName = null; + + // First look for a SOAPAction header. + // this is what .net uses to identify the operation + MimeHeaders headers = message.getMimeHeaders(); + if (headers != null) { + String[] actions = headers.getHeader("SOAPAction"); + if (actions != null && actions.length > 0) { + opName = actions[0]; + if (opName != null && opName.equals("\"\"")) { + opName = null; + } + } + } + + // If that doesn't work then we default to trying the name + // of the first child element of the SOAP envelope. + if (opName == null) { + Name name = getName(message); + if (name != null) { + opName = name.getLocalName(); + } + } + + return opName; + } + + private Name getName(SOAPMessage message) { + SOAPPart soap = message.getSOAPPart(); + if (soap == null) { + return null; + } + + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope == null) { + return null; + } + + SOAPBody body = envelope.getBody(); + if (body == null) { + return null; + } + + Iterator childElements = body.getChildElements(); + while (childElements.hasNext()) { + if (childElements.next() instanceof SOAPElement soapElement) { + return soapElement.getElementName(); + } + } + } catch (SOAPException se) { + logger.log(FINE, "WSS: Unable to get SOAP envelope", se); + } + + return null; + } + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java index 8f27fe7..c36386e 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java @@ -16,6 +16,8 @@ package ee.jakarta.tck.authentication.test.basic.sam.config; +import static java.util.logging.Level.INFO; + import ee.jakarta.tck.authentication.test.basic.sam.AuthDataCallbackHandler; import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; @@ -27,7 +29,6 @@ import jakarta.security.auth.message.config.ServerAuthConfig; import java.util.HashMap; import java.util.Map; -import java.util.logging.Level; import javax.security.auth.callback.CallbackHandler; /** @@ -37,7 +38,7 @@ public class TSAuthConfigProvider implements jakarta.security.auth.message.config.AuthConfigProvider { private static TSLogger logger; - private static Map properties; + private static Map properties; private HashMap clientAuthConfigMap = new HashMap(); private HashMap serverAuthConfigMap = new HashMap(); @@ -103,7 +104,7 @@ public TSAuthConfigProvider(Map props, AuthConfigFactory factory, TSLogger tsLog public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { String logStr = "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=" + layer + " : " + "appContext=" + appContext; - logger.log(Level.INFO, logStr); + logger.log(INFO, logStr); try { if (handler == null) { @@ -160,7 +161,7 @@ public ClientAuthConfig getClientAuthConfig(String layer, String appContext, Cal public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { String logStr = "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=" + layer + " : " + "appContext=" + appContext; - logger.log(Level.INFO, logStr); + logger.log(INFO, logStr); try { if ((!layer.equals(JASPICData.LAYER_SERVLET)) && (handler == null)) { @@ -173,13 +174,13 @@ public ServerAuthConfig getServerAuthConfig(String layer, String appContext, Cal // that we should NOT have a null cbh passed in String msg = "FAILURE: layer=" + layer + " appContext=" + appContext; msg += " getServerAuthConfig() received CallbackHandler=null"; - logger.log(Level.INFO, msg); + logger.log(INFO, msg); } ServerAuthConfig serverAuthConfig = null; if (JASPICData.LAYER_SOAP.equals(layer)) { - // serverAuthConfig = new SOAPTSServerAuthConfig(layer, appContext, handler, properties, logger); + serverAuthConfig = new SOAPTSServerAuthConfig(layer, appContext, handler, properties, logger); } else { serverAuthConfig = new TSServerAuthConfig(layer, appContext, handler, properties, logger); } diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java index 5da45ba..db66f38 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java @@ -16,15 +16,21 @@ package ee.jakarta.tck.authentication.test.basic.sam.config; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static java.util.logging.Level.INFO; + import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSClientAuthModule; -import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSAuthExceptionClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSFailureClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendFailureClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendSuccessClientAuthModule; import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; import jakarta.security.auth.message.AuthException; import jakarta.security.auth.message.AuthStatus; import jakarta.security.auth.message.MessageInfo; import jakarta.security.auth.message.module.ClientAuthModule; import java.util.Map; -import java.util.logging.Level; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; @@ -36,65 +42,53 @@ public class TSClientAuthContext implements jakarta.security.auth.message.config private static ClientAuthModule clientAuthModule; private static TSLogger logger; - private static String messageLayer = null; - private static String appContext = null; - private static CallbackHandler handler = null; - private static String operation = null; - private static Subject clientSubject = null; - private static Map properties = null; public TSClientAuthContext() { } public TSClientAuthContext(String messageLayer, String appContext, CallbackHandler handler, String operation, Subject clientSubject, - Map properties, TSLogger tsLogger) throws AuthException { + Map properties, TSLogger tsLogger) throws AuthException { this(messageLayer, appContext, handler, operation, clientSubject, properties); + logger = tsLogger; ClientAuthModule cam = null; - logger.log(Level.INFO, "TSClientAuthContext called"); + logger.log(INFO, "TSClientAuthContext called"); - // pass TSlogger to TSServerAuthModule through properties + // Pass TSlogger to TSServerAuthModule through properties properties.put("TSLogger", logger); - if (messageLayer.equals(JASPICData.LAYER_SOAP)) { -// if (appContext.indexOf("SendSuccessHello") > -1) { -// cam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSSendSuccessClientAuthModule(); -// cam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("SendFailureHello") > -1) { -// cam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSSendFailureClientAuthModule(); -// cam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("FailureHello") > -1) { -// cam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSFailureClientAuthModule(); -// cam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("AuthExceptionHello") > -1) { -// cam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSAuthExceptionClientAuthModule(); -// cam.initialize(null, null, handler, properties); -// -// } else { -// cam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSClientAuthModule(); -// cam.initialize(null, null, handler, properties); -// } - } else if (messageLayer.equals(JASPICData.LAYER_SERVLET)) { + if (messageLayer.equals(LAYER_SOAP)) { + if (appContext.indexOf("SendSuccessHello") > -1) { + cam = new TSSendSuccessClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("SendFailureHello") > -1) { + cam = new TSSendFailureClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("FailureHello") > -1) { + cam = new TSFailureClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("AuthExceptionHello") > -1) { + cam = new TSAuthExceptionClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else { + cam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSClientAuthModule(); + cam.initialize(null, null, handler, properties); + } + } else if (messageLayer.equals(LAYER_SERVLET)) { cam = new TSClientAuthModule(); cam.initialize(null, null, handler, properties); } clientAuthModule = cam; - } - private TSClientAuthContext(String layer, String appContxt, CallbackHandler hndler, String operatn, Subject cliSubject, Map props) throws AuthException { - messageLayer = layer; - appContext = appContxt; - handler = hndler; - operation = operatn; - clientSubject = cliSubject; - properties = props; + private TSClientAuthContext(String layer, String appContxt, CallbackHandler hndler, String operatn, Subject cliSubject, Map props) throws AuthException { } @@ -142,7 +136,7 @@ private TSClientAuthContext(String layer, String appContxt, CallbackHandler hndl */ @Override public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { - logger.log(Level.INFO, "TSClientAuthContext.secureRequest called"); + logger.log(INFO, "TSClientAuthContext.secureRequest called"); return clientAuthModule.secureRequest(messageInfo, clientSubject); } @@ -193,7 +187,8 @@ public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) */ @Override public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { - logger.log(Level.INFO, "TSClientAuthContext.validateResponse called"); + logger.log(INFO, "TSClientAuthContext.validateResponse called"); + return clientAuthModule.validateResponse(messageInfo, clientSubject, serviceSubject); } diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java index acb6c86..6f4902e 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java @@ -16,10 +16,17 @@ package ee.jakarta.tck.authentication.test.basic.sam.config; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.SVC_SUBJECT_KEY; import static java.util.logging.Level.INFO; import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSServerAuthModule; import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSServletWrapperSAM; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSAuthExceptionServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSFailureServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendFailureServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendSuccessServerAuthModule; import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; import jakarta.security.auth.message.AuthException; @@ -46,9 +53,12 @@ public class TSServerAuthContext implements jakarta.security.auth.message.config private static ServerAuthModule serverAuthModule; private static String messageLayer; - private static Map properties; + private static Map properties; private static TSLogger logger; + + private static String soapUPTokenAppContext = "HelloService HelloPort"; private static String servletUPTokenAppContext = "spitests_servlet_web"; + private static MessageInfo messageInfoFromVerifyReq; private static boolean isMandatory = false; @@ -100,40 +110,38 @@ public String getID() { // Note: We could also choose auth modules based on appContext but // MessageLayer seems to be the ideal candidate for choosing // auth modules. - if (messageLayer.equals(JASPICData.LAYER_SOAP)) { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSServerAuthModule(); -// -// if (appContext.equals(soapUPTokenAppContext)) { -// -// sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); -// } else if (appContext.indexOf("SendSuccessHello") > -1) { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSSendSuccessServerAuthModule(); -// sam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("SendFailureHello") > -1) { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSSendFailureServerAuthModule(); -// sam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("FailureHello") > -1) { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSFailureServerAuthModule(); -// sam.initialize(null, null, handler, properties); -// -// } else if (appContext.indexOf("AuthExceptionHello") > -1) { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSAuthExceptionServerAuthModule(); -// sam.initialize(null, null, handler, properties); -// -// } else { -// sam = new com.sun.ts.tests.jaspic.tssv.module.soap.TSServerAuthModule(); -// sam.initialize(null, null, handler, properties); -// } - - } else if (messageLayer.equals(JASPICData.LAYER_SERVLET)) { + if (messageLayer.equals(LAYER_SOAP)) { + + if (appContext.equals(soapUPTokenAppContext)) { + sam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSServerAuthModule(); + sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); + } else if (appContext.indexOf("SendSuccessHello") > -1) { + sam = new TSSendSuccessServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("SendFailureHello") > -1) { + sam = new TSSendFailureServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("FailureHello") > -1) { + sam = new TSFailureServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("AuthExceptionHello") > -1) { + sam = new TSAuthExceptionServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else { + sam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSServerAuthModule(); + sam.initialize(null, null, handler, properties); + } + + } else if (messageLayer.equals(LAYER_SERVLET)) { System.out.println("AppContext =" + appContext); if (appContext.contains(servletUPTokenAppContext) && operation.contains("WrapperServlet")) { sam = new TSServletWrapperSAM(); sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); - } else if (appContext.contains(servletUPTokenAppContext)) { sam = new TSServerAuthModule(); sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); @@ -149,7 +157,7 @@ public String getID() { /* * This should be private so that we can be sure people pass a valid TSLogger into the public constructor. */ - private TSServerAuthContext(String layer, String appCtxt, CallbackHandler callbackHandler, String optn, Subject clientSubject, Map props) throws AuthException { + private TSServerAuthContext(String layer, String appCtxt, CallbackHandler callbackHandler, String optn, Subject clientSubject, Map props) throws AuthException { messageLayer = layer; properties = props; } @@ -525,37 +533,31 @@ public void verifyMessageInfoObjsMatch(MessageInfo messageInfo) { * */ public void verifySecureRespServiceSubject(Subject serviceSubject) { - String msg = ""; - String key = JASPICData.SVC_SUBJECT_KEY; - Subject value = (Subject) properties.get(key); + Subject subjectFromProperties = (Subject) properties.get(SVC_SUBJECT_KEY); - if (value != null) { - msg = "got a non-null subject out of the map"; - logger.log(INFO, msg); + if (subjectFromProperties != null) { + logger.log(INFO, "Got a non-null subject out of the map"); if (serviceSubject == null) { - msg = "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."; - } else if (value.equals(serviceSubject)) { - msg = "SecureResponse ServiceSubjects correctly matched."; + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."); + } else if (subjectFromProperties.equals(serviceSubject)) { + logger.log(INFO, "SecureResponse ServiceSubjects correctly matched."); } else { - // if here, serviceSubjects dont match but should - msg = "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."; + // If here, service subjects don't match but should + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."); } - logger.log(INFO, msg); - if ((serviceSubject != null) && (value.equals(serviceSubject))) { - // to help verify assertion JASPI:SPEC:313 (per spec section 2.1.5.2): + if ((serviceSubject != null) && (subjectFromProperties.equals(serviceSubject))) { + // To help verify assertion JASPI:SPEC:313 (per spec section 2.1.5.2): // If a non-null Subject was used to call the ServerAuthContext // the same Subject must be passed as the serviceSubject in this // call. If a non-null serviceSubject is used in this call, it // must not be read-only if (serviceSubject.isReadOnly()) { - // should not get here. - msg = "FAILURE detected - SecureResponse ServiceSubjects should not be read-only."; - logger.log(INFO, msg); + // Should not get here. + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should not be read-only."); } else { - msg = "Valid SecureResponse ServiceSubjects - it was not read-only."; - logger.log(INFO, msg); + logger.log(INFO, "Valid SecureResponse ServiceSubjects - it was not read-only."); } } } @@ -577,6 +579,7 @@ public void verifySecureRespServiceSubject(Subject serviceSubject) { @Override public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { logger.log(INFO, "TSServerAuthContext.cleanSubject called"); + serverAuthModule.cleanSubject(messageInfo, subject); } diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/SamAutoRegistrationListener.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java similarity index 58% rename from tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/SamAutoRegistrationListener.java rename to tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java index 1d942cf..2fc4315 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/SamAutoRegistrationListener.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2022 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -13,29 +13,20 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ -package ee.jakarta.tck.authentication.test.basic.sam; +package ee.jakarta.tck.authentication.test.basic.servlet; import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactory; import jakarta.security.auth.message.config.AuthConfigFactory; -import jakarta.servlet.ServletContextEvent; -import jakarta.servlet.ServletContextListener; -import jakarta.servlet.annotation.WebListener; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import java.util.Set; -/** - * - * @author Arjan Tijms - * - */ -@WebListener -public class SamAutoRegistrationListener implements ServletContextListener { +public class AuthFactoryContainerInitializer implements ServletContainerInitializer { @Override - public void contextInitialized(ServletContextEvent sce) { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { AuthConfigFactory.setFactory(new TSAuthConfigFactory()); - - -// AuthConfigFactory.getFactory() -// .registerServerAuthModule(new TestServerAuthModule(), sce.getServletContext()); } -} \ No newline at end of file +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java index 0d670db..f5a5d3b 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java @@ -21,6 +21,8 @@ import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.enterprise.concurrent.ManagedExecutorDefinition; +import jakarta.enterprise.concurrent.ManagedScheduledExecutorDefinition; import java.util.Collection; import java.util.Iterator; import java.util.logging.Level; diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java index 2977ff9..352caa8 100644 --- a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java @@ -16,7 +16,11 @@ package ee.jakarta.tck.authentication.test.basic.servlet; +import jakarta.annotation.Resource; import jakarta.annotation.security.DeclareRoles; +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.ManagedExecutorDefinition; +import jakarta.enterprise.concurrent.ManagedExecutorService; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HttpConstraint; import jakarta.servlet.annotation.HttpMethodConstraint; diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java new file mode 100644 index 0000000..833e58f --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java @@ -0,0 +1,42 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebParam; +import jakarta.jws.WebResult; +import jakarta.jws.WebService; +import jakarta.xml.bind.annotation.XmlSeeAlso; +import jakarta.xml.ws.Action; +import jakarta.xml.ws.RequestWrapper; +import jakarta.xml.ws.ResponseWrapper; + + +/** + * This class was generated by the XML-WS Tools. + * XML-WS Tools 4.0.1 + * Generated source version: 3.0 + * + */ +@WebService(name = "Hello", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/") +@XmlSeeAlso({ + ObjectFactory.class +}) +public interface Hello { + + + /** + * + * @param arg0 + * @return + * returns java.lang.String + */ + @WebMethod + @WebResult(targetNamespace = "") + @RequestWrapper(localName = "sayHelloProtected", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", className = "ee.jakarta.tck.authentication.test.basic.soap.SayHelloProtected") + @ResponseWrapper(localName = "sayHelloProtectedResponse", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", className = "ee.jakarta.tck.authentication.test.basic.soap.SayHelloProtectedResponse") + @Action(input = "http://soap.basic.test.authentication.tck.jakarta.ee/Hello/sayHelloProtectedRequest", output = "http://soap.basic.test.authentication.tck.jakarta.ee/Hello/sayHelloProtectedResponse") + public String sayHelloProtected( + @WebParam(name = "arg0", targetNamespace = "") + String arg0); + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java new file mode 100644 index 0000000..311f85a --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java @@ -0,0 +1,94 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import java.net.MalformedURLException; +import java.net.URL; +import javax.xml.namespace.QName; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.WebEndpoint; +import jakarta.xml.ws.WebServiceClient; +import jakarta.xml.ws.WebServiceException; +import jakarta.xml.ws.WebServiceFeature; + + +/** + * This class was generated by the XML-WS Tools. + * XML-WS Tools 4.0.1 + * Generated source version: 3.0 + * + */ +@WebServiceClient(name = "HelloService", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", wsdlLocation = "http://localhost:8080/spitests_servlet_web/HelloService?wsdl") +public class HelloService + extends Service +{ + + private static final URL HELLOSERVICE_WSDL_LOCATION; + private static final WebServiceException HELLOSERVICE_EXCEPTION; + private static final QName HELLOSERVICE_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService"); + + static { + URL url = null; + WebServiceException e = null; + try { + url = new URL("http://localhost:8080/spitests_servlet_web/HelloService?wsdl"); + } catch (MalformedURLException ex) { + e = new WebServiceException(ex); + } + HELLOSERVICE_WSDL_LOCATION = url; + HELLOSERVICE_EXCEPTION = e; + } + + public HelloService() { + super(__getWsdlLocation(), HELLOSERVICE_QNAME); + } + + public HelloService(WebServiceFeature... features) { + super(__getWsdlLocation(), HELLOSERVICE_QNAME, features); + } + + public HelloService(URL wsdlLocation) { + super(wsdlLocation, HELLOSERVICE_QNAME); + } + + public HelloService(URL wsdlLocation, WebServiceFeature... features) { + super(wsdlLocation, HELLOSERVICE_QNAME, features); + } + + public HelloService(URL wsdlLocation, QName serviceName) { + super(wsdlLocation, serviceName); + } + + public HelloService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) { + super(wsdlLocation, serviceName, features); + } + + /** + * + * @return + * returns Hello + */ + @WebEndpoint(name = "HelloPort") + public Hello getHelloPort() { + return super.getPort(new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloPort"), Hello.class); + } + + /** + * + * @param features + * A list of {@link jakarta.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the features parameter will have their default values. + * @return + * returns Hello + */ + @WebEndpoint(name = "HelloPort") + public Hello getHelloPort(WebServiceFeature... features) { + return super.getPort(new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloPort"), Hello.class, features); + } + + private static URL __getWsdlLocation() { + if (HELLOSERVICE_EXCEPTION!= null) { + throw HELLOSERVICE_EXCEPTION; + } + return HELLOSERVICE_WSDL_LOCATION; + } + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java new file mode 100644 index 0000000..2c49164 --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java @@ -0,0 +1,83 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import javax.xml.namespace.QName; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.annotation.XmlElementDecl; +import jakarta.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the ee.jakarta.tck.authentication.test.basic.soap package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private static final QName _SayHelloProtected_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "sayHelloProtected"); + private static final QName _SayHelloProtectedResponse_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "sayHelloProtectedResponse"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: ee.jakarta.tck.authentication.test.basic.soap + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link SayHelloProtected } + * + * @return + * the new instance of {@link SayHelloProtected } + */ + public SayHelloProtected createSayHelloProtected() { + return new SayHelloProtected(); + } + + /** + * Create an instance of {@link SayHelloProtectedResponse } + * + * @return + * the new instance of {@link SayHelloProtectedResponse } + */ + public SayHelloProtectedResponse createSayHelloProtectedResponse() { + return new SayHelloProtectedResponse(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SayHelloProtected }{@code >} + * + * @param value + * Java instance representing xml element's value. + * @return + * the new instance of {@link JAXBElement }{@code <}{@link SayHelloProtected }{@code >} + */ + @XmlElementDecl(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", name = "sayHelloProtected") + public JAXBElement createSayHelloProtected(SayHelloProtected value) { + return new JAXBElement<>(_SayHelloProtected_QNAME, SayHelloProtected.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SayHelloProtectedResponse }{@code >} + * + * @param value + * Java instance representing xml element's value. + * @return + * the new instance of {@link JAXBElement }{@code <}{@link SayHelloProtectedResponse }{@code >} + */ + @XmlElementDecl(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", name = "sayHelloProtectedResponse") + public JAXBElement createSayHelloProtectedResponse(SayHelloProtectedResponse value) { + return new JAXBElement<>(_SayHelloProtectedResponse_QNAME, SayHelloProtectedResponse.class, null, value); + } + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java new file mode 100644 index 0000000..c052d6f --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java @@ -0,0 +1,60 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for sayHelloProtected complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

{@code
+ * 
+ *   
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "sayHelloProtected", propOrder = { + "arg0" +}) +public class SayHelloProtected { + + protected String arg0; + + /** + * Gets the value of the arg0 property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getArg0() { + return arg0; + } + + /** + * Sets the value of the arg0 property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setArg0(String value) { + this.arg0 = value; + } + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java new file mode 100644 index 0000000..47e8d0e --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java @@ -0,0 +1,62 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for sayHelloProtectedResponse complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

{@code
+ * 
+ *   
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "sayHelloProtectedResponse", propOrder = { + "_return" +}) +public class SayHelloProtectedResponse { + + @XmlElement(name = "return") + protected String _return; + + /** + * Gets the value of the return property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReturn() { + return _return; + } + + /** + * Sets the value of the return property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReturn(String value) { + this._return = value; + } + +} diff --git a/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java new file mode 100644 index 0000000..0cb79e8 --- /dev/null +++ b/tck/profile-spi/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java @@ -0,0 +1,2 @@ +@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/") +package ee.jakarta.tck.authentication.test.basic.soap; diff --git a/tck/profile-spi/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook b/tck/profile-spi/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook new file mode 100644 index 0000000..1d320b3 --- /dev/null +++ b/tck/profile-spi/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook @@ -0,0 +1 @@ +com.sun.enterprise.security.webservices.client.ClientSecurityPipeCreator \ No newline at end of file diff --git a/tck/profile-spi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/tck/profile-spi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 0000000..7a4388c --- /dev/null +++ b/tck/profile-spi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer \ No newline at end of file diff --git a/tck/profile-spi/src/main/webapp/WEB-INF/HelloService.wsdl b/tck/profile-spi/src/main/webapp/WEB-INF/HelloService.wsdl index ae434bf..7a83418 100644 --- a/tck/profile-spi/src/main/webapp/WEB-INF/HelloService.wsdl +++ b/tck/profile-spi/src/main/webapp/WEB-INF/HelloService.wsdl @@ -3,12 +3,12 @@ RI's version is Eclipse Metro/4.0.0-M4 (RELEASE-4.0.0-M4-3da3fb9; 2022-03-31T12:28:52+0000) XMLWS-Impl/4.0.0-M4 XMLWS-API/4.0.0 XMLB-Impl/4.0.0-M4 XMLB-API/4.0.0 git-revision#3da3fb9. --> @@ -16,7 +16,7 @@ @@ -32,10 +32,10 @@ diff --git a/tck/profile-spi/src/main/webapp/WEB-INF/HelloService_schema1.xsd b/tck/profile-spi/src/main/webapp/WEB-INF/HelloService_schema1.xsd index da88ee9..94b5d77 100644 --- a/tck/profile-spi/src/main/webapp/WEB-INF/HelloService_schema1.xsd +++ b/tck/profile-spi/src/main/webapp/WEB-INF/HelloService_schema1.xsd @@ -1,5 +1,5 @@ - + diff --git a/tck/profile-spi/src/main/webapp/WEB-INF/soap-web.xml b/tck/profile-spi/src/main/webapp/WEB-INF/soap-web.xml new file mode 100644 index 0000000..c8cbc07 --- /dev/null +++ b/tck/profile-spi/src/main/webapp/WEB-INF/soap-web.xml @@ -0,0 +1,49 @@ + + + + + + soap_app + + + Hello + ee.jakarta.tck.authentication.test.basic.soap.HelloImpl + 0 + + ADM + Administrator + + + + Hello + /Hello + + + + 54 + + + + the administrator role + Administrator + + diff --git a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java index c5935f7..29ec0fd 100644 --- a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java +++ b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java @@ -3,13 +3,16 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer; import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; import ee.jakarta.tck.authentication.test.common.ArquillianBase; import ee.jakarta.tck.authentication.test.common.logging.client.LogFileProcessor; +import jakarta.servlet.ServletContainerInitializer; import org.htmlunit.WebResponse; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,7 +40,12 @@ public class ServletProfileSPITest extends ArquillianBase { @Deployment(testable = false) public static Archive createDeployment() { - return defaultWebArchive("spitests_servlet_web"); + WebArchive archive = defaultWebArchive("spitests_servlet_web"); + archive.addAsServiceProvider(ServletContainerInitializer.class, AuthFactoryContainerInitializer.class); + + System.out.println(archive.toString(true)); + + return archive; } /** diff --git a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/UnitTest.java b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java similarity index 99% rename from tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/UnitTest.java rename to tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java index c4d0a15..205d1d0 100644 --- a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/UnitTest.java +++ b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java @@ -41,9 +41,9 @@ * @author Arjan Tijms * */ -public class UnitTest { +public class ServletUnitTest { - Logger logger = Logger.getLogger(UnitTest.class.getName()); + Logger logger = Logger.getLogger(ServletUnitTest.class.getName()); /** * diff --git a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java new file mode 100644 index 0000000..b0864dc --- /dev/null +++ b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java @@ -0,0 +1,866 @@ +package ee.jakarta.tck.authentication.test.basic; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.TSSV_ACF; +import static jakarta.security.auth.message.config.AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY; +import static org.junit.Assert.assertTrue; + +import com.sun.xml.ws.api.client.ClientPipelineHook; +import ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.basic.soap.HelloService; +import ee.jakarta.tck.authentication.test.common.ArquillianBase; +import ee.jakarta.tck.authentication.test.common.logging.client.LogFileProcessor; +import ee.jakarta.tck.authentication.test.common.logging.client.TestUtil; +import jakarta.servlet.ServletContainerInitializer; +import java.net.URL; +import java.security.Security; +import java.util.Optional; +import java.util.ServiceLoader; +import javax.xml.namespace.QName; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +/** + * This tests a large number of SPI assertions from the Servlet profile. + * + * @author Arjan Tijms + * + */ +@RunWith(Arquillian.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SoapProfileSPITest extends ArquillianBase { + + private static boolean setUpIsDone = false; + + String logicalHostName = "localhost"; + String expectedAppContextId = "localhost /Hello_web/Hello"; + + // this must be the decoded context path corresponding to the web module + private String contextPath = "/" + JASPICData.SCP_CONTEXT_PATH; + private String openToAllServletPath = "OpenToAllServlet"; + + private String appContext = System.getProperty("logical.hostname.servlet") + " " + contextPath; + + LogFileProcessor logProcessor = new LogFileProcessor(); + LogFileProcessor clientLogProcessor = new LogFileProcessor(true); + + @Deployment(testable = false) + public static Archive createDeployment() { + WebArchive archive = defaultWebArchive("Hello_web"); + archive.addAsServiceProvider(ServletContainerInitializer.class, AuthFactoryContainerInitializer.class); + archive.addAsWebInfResource(resource("soap-web.xml"), "web.xml"); + + System.out.println(archive.toString(true)); + + return archive; + } + + + @Before + public void runOnce() throws Exception { + if (setUpIsDone) { + return; + } + + Security.setProperty(DEFAULT_FACTORY_SECURITY_PROPERTY, TSSV_ACF); + Optional optionalHook = ServiceLoader.load(ClientPipelineHook.class).findFirst(); + if (optionalHook.isPresent()) { + int a; + a = 4; + } + + HelloService helloService = new HelloService( + new URL(getBase(), "Hello?wsdl"), + new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService")); + + String text = helloService.getHelloPort().sayHelloProtected("Raja"); + TestUtil.logMsg("Got Output : " + text); + + logProcessor.fetchLogs(); + + setUpIsDone = true; + } + + @Before + public void fetchLogs() { + logProcessor.fetchLogs(); + clientLogProcessor.fetchLogs(); + } + + + // ### Client side tests + + /** + * @keywords: jaspic_soap + * + * @testName: ValidateResponse + * + * @assertion_ids: JASPIC:SPEC:42 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether ClientAuthContext.validateResponse() is called + * + * Description The runtime must invoke ClientAuthContext.validateResponse() + * + */ + @Test + public void ValidateResponse() { + // verify whether the log contains required messages. + assertTrue( + "validateResponse failed : " + "ClientAuthContext.validateResponse not called", + clientLogProcessor.verifyLogContains("TSClientAuthContext.validateResponse called")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAuthContext + * + * @assertion_ids: JASPIC:SPEC:34; JASPIC:JAVADOC:72; JASPIC:JAVADOC:97; JASPIC:JAVADOC:98 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether ClientAuthConfig.getAuthContext() is called + * + * Description The runtime must invoke clientAuthConfig().getAuthContext() to obtain the ClientAuthContext. + * + */ + @Test + public void ClientAuthContext() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + "ClientAuthContext failed : " + "clientAuthConfig.getAuthContext not called", + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSClientAuthConfig.getAuthContext: layer=SOAP : appContext=")); + } + + /** + * @testName: NameAndPasswordCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:123; JASPIC:JAVADOC:103 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether CallbackHandler for client runtime supports + * NameCallback and PasswordCallback + * + * Description Unless the client runtime is embedded in a server runtime (e.g.; an invocation of a web service by a + * servlet running in a Servlet container), The CallbackHandler passed to ClientAuthModule.initialize must support the + * following callbacks: NameCallback, PasswordCallback + * + */ + @Test + public void NameAndPasswordCallbackSupport() { + // verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "In SOAP : ClientRuntime CallbackHandler supports NameCallback", + "In SOAP : ClientRuntime CallbackHandler supports PasswordCallback")); + } + + /** + * @testName: ClientRuntimeCommonCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:JAVADOC:35; JASPIC:JAVADOC:36; JASPIC:JAVADOC:49; JASPIC:JAVADOC:51; + * JASPIC:JAVADOC:54; JASPIC:JAVADOC:63; JASPIC:JAVADOC:65; JASPIC:JAVADOC:68; JASPIC:JAVADOC:69; JASPIC:JAVADOC:71; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether CallbackHandler for client runtime supports + * CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * Description + * + * The CallbackHandler passed to the initialize method of an authentication module should support the following + * callbacks, and it must be possible to configure the runtime such that the CallbackHandler passed at module + * initialization module supports the following callbacks (in addition to any others required to be supported by the + * applicable internal profile): CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * + */ + @Test + public void ClientRuntimeCommonCallbackSupport() { + // verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "In SOAP : ClientRuntime CallbackHandler supports CertStoreCallback", + "In SOAP : ClientRuntime CallbackHandler supports PrivateKeyCallback", + "In SOAP : ClientRuntime CallbackHandler supports SecretKeyCallback", + "In SOAP : ClientRuntime CallbackHandler supports TrustStoreCallback")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPClientAuthConfig + * + * @assertion_ids: JASPIC:SPEC:124; JASPIC:JAVADOC:77 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether the arguments(layer and appcontextId) passed to + * obtain AuthConfigProvider is same as the arguments used in calling getClientAuthConfig. + * + * Description + * + * If a non-null AuthConfigProvider is returned (by the call to getConfigProvider), the messaging runtime must call + * getClientAuthConfig on the provider to obtain the authentication context configuration object pertaining to the + * application context at the layer. The layer and appContext arguments of the call to getClientAuthConfig must be the + * same as those used to acquire the provider. + * + * + */ + @Test + public void ACPClientAuthConfig() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :")); + + // ACPClientAuthConfig : Same layer and appContextId used + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: CACRequestResponse + * + * @assertion_ids: JASPIC:SPEC:130; JASPIC:JAVADOC:7; JASPIC:JAVADOC:9; JASPIC:SPEC:19; JASPIC:SPEC:23; JASPIC:SPEC:129; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether for a non-null ClientAuthContext, secureRequest + * and validateResponse are called properly. + * + * Description + * + * If the client runtime obtained a non-null ClientAuthContext by using the operation identifier corresponding to the + * request message, then at point (1) in the message processing model, the runtime must call secureRequest on the + * ClientAuthContext, and at point (4) the runtime must call validateResponse on the ClientAuthContext. + * + * + */ + @Test + public void CACRequestResponse() { + // Verify whether the log contains required messages. + assertTrue(clientLogProcessor.verifyLogContains( + "TSClientAuthConfig.getAuthContext: returned non-null" + " ClientAuthContext for operationId=sayHelloProtected", + "TSClientAuthContext.secureRequest called", + "TSClientAuthContext.validateResponse called")); + } + + + + + /** + * @keywords: jaspic_soap + * + * @testName: ClientRuntimeMessageInfoMap + * + * @assertion_ids: JASPIC:SPEC:133; JASPIC:JAVADOC:7 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether the Map in messageInfo object passed to + * secureRequest and validateResponse contains the right value for key jakarta.xml.ws.wsdl.service + * + * Description This profile requires that the message processing runtime establish the following key-value pairs within + * the Map of the MessageInfo passed in the calls to secureRequest and validateResponse Key=jakarta.xml.ws.wsdl.service + * Value= the value of the qualified service name, represented as a javax.xml.namespace.QName + * + */ + @Test + public void ClientRuntimeMessageInfoMap() { + QName expectedQName = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService"); + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "TSClientAuthModule.secureRequest messageInfo :" + "jakarta.xml.ws.wsdl.service=" + expectedQName.toString(), + "TSClientAuthModule.validateResponse messageInfo :" + "jakarta.xml.ws.wsdl.service=" + expectedQName.toString())); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAppContextId + * + * @assertion_ids: JASPIC:SPEC:208 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether for the client side appilcation context + * Identifier is correctly used by the runtime. + * + * Description A Client application context Identifier must be the String value composed by concatenating the client + * scope identifier, a blank separator character, and the client reference to the service. The clien scope identifier is + * not testable but we can check the client reference to the service. + * + */ + @Test + public void ClientAppContextId() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAuthConfig + * + * @assertion_ids: JASPIC:SPEC:11; JASPIC:SPEC:12 ; JASPIC:SPEC:16; + * JASPIC:JAVADOC:92; JASPIC:JAVADOC:93; JASPIC:SPEC:110; + * JASPIC:SPEC:111 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether AuthConfigProvider.ClientAuthConfig is + * called in the server. + * + * Description The runtime must invoke + * AuthConfigProvider.getClientAuthConfig() to obtain the + * AuthConfig. The runtime must also specify + * appropriate(non-null) layer(i.e for this test case "SOAP" + * layer) and application context identifiers in its call to + * getClientAuthConfig. + * + */ + @Test + public void ClientAuthConfig() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // verify whether the log contains required messages. + assertTrue( + "ClientAuthConfig failed : " + "AuthConfigProvider.getClientAuthConfig not called", + clientLogProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getClientAuthConfig called for layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SecureRequest + * + * @assertion_ids: JASPIC:SPEC:35; JASPIC:JAVADOC:5; JASPIC:SPEC:36; + * JASPIC:JAVADOC:75 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ClientAuthContext.secureRequest() is called + * + * Description The runtime must invoke + * ClientAuthContext.secureRequest() + * + */ + @Test + public void SecureRequest() { + // Verify whether the log contains required messages. + assertTrue( + "SecureRequest failed : " + "ClientAuthContext.secureRequest not called", + clientLogProcessor.verifyLogContains("TSClientAuthContext.secureRequest called")); + } + + + + // ### Server side ### + + /** + * @keywords: jaspic_soap + * + * @testName: GetConfigProvider + * + * @assertion_ids: JASPIC:SPEC:8; JASPIC:SPEC:14; JASPIC:SPEC:116; JASPIC:SPEC:117; JASPIC:JAVADOC:77; + * JASPIC:JAVADOC:79; JASPIC:JAVADOC:80; JASPIC:JAVADOC:84; JASPIC:JAVADOC:85; JASPIC:JAVADOC:91; JASPIC:SPEC:110; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether AuthConfigFactory.getConfigProvider is called + * in the server. + * + * Description The runtime must invoke AuthConfigFactory.getConfigProvider to obtain the AuthConfigProvider. The runtime + * must also specify appropriate(non-null) layer and application context identifiers in its call to getConfigProvider. + * + */ + @Test + public void GetConfigProvider() { + assertTrue( + "GetConfigProvider failed : " + "AuthConfigFactory.getConfigProvider not called", + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider called", + "getConfigProvider called for Layer : SOAP and AppContext :" + expectedAppContextId )); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: GetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:77; + * JASPIC:JAVADOC:80; JASPIC:JAVADOC:84; JASPIC:JAVADOC:79; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether AuthConfigFactory.getConfigProvider is + * called in the server. + * + * Description The runtime must invoke + * AuthConfigFactory.getConfigProvider to obtain the + * AuthConfigProvider. By calling getConfigProvider, we can + * assume getFactory() was called. + * + */ + @Test + public void GetFactory() { + // Verify whether the log contains required messages. + assertTrue( + "GetFactory failed : " + "AuthConfigFactory.getFactory not called", + logProcessor.verifyLogContains("TSAuthConfigFactory.getFactory called Indirectly")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAuthConfig + * + * @assertion_ids: JASPIC:SPEC:11; JASPIC:SPEC:13 ; JASPIC:SPEC:16; + * JASPIC:JAVADOC:95 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether provider.getServerAuthConfig is called in + * the server. + * + * Description The runtime must invoke + * AuthConfigProvider.getServerAuthConfig() to obtain the + * AuthConfig. The runtime must also specify + * appropriate(non-null) layer(i.e for this test case "SOAP" + * layer) and application context identifiers in its call to + * getServerAuthConfig. + * + */ + @Test + public void ServerAuthConfig() { + // Verify whether the log contains required messages. + assertTrue( + "ServerAuthConfig failed : " + "AuthConfigProvider.getServerAuthConfig not called", + logProcessor.verifyLogContains( + "TSAuthConfigProvider.getServerAuthConfig called for layer=SOAP" + + " : appContext=" + expectedAppContextId)); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ValidateRequest + * + * @assertion_ids: JASPIC:SPEC:50; JASPIC:JAVADOC:16; JASPIC:JAVADOC:17; + * JASPIC:JAVADOC:23; JASPIC:SPEC:23; JASPIC:SPEC:19 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthContext.validateRequest() is + * called + * + * Description The runtime must invoke + * ServerAuthContext.validateRequest() + * + */ + @Test + public void ValidateRequest() { + // Verify whether the log contains required messages. + assertTrue( + "ValidateRequest failed : " + "ServerAuthContext.validateRequest not called", + logProcessor.verifyLogContains("TSServerAuthContext.validateRequest called")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SecureResponse + * + * @assertion_ids: JASPIC:SPEC:59; JASPIC:JAVADOC:16; JASPIC:JAVADOC:17; + * JASPIC:JAVADOC:23; JASPIC:JAVADOC:26 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthContext.secureResponse() is called + * + * Description The runtime must invoke + * ServerAuthContext.secureResponse() + * + */ + @Test + public void SecureResponse() { + // verify whether the log contains required messages. + assertTrue("SecureResponse failed : " + "ServerAuthContext.secureResponse not called", + logProcessor.verifyLogContains("TSServerAuthContext.secureResponse called")); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAuthContext + * + * @assertion_ids: JASPIC:SPEC:34; JASPIC:JAVADOC:100; JASPIC:SPEC:153; + * JASPIC:SPEC:156; JASPIC:JAVADOC:101 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthConfig.getAuthContext() is called + * + * Description The runtime must invoke + * serverAuthConfig().getAuthContext() to obtain the + * ServerAuthContext. + * + */ + @Test + public void ServerAuthContext() { + // verify whether the log contains required messages. + assertTrue( + "TSServerAuthConfig.getAuthContext: layer=SOAP : appContext=" + expectedAppContextId, + logProcessor.verifyLogContains( + "TSServerAuthConfig.getAuthContext: layer=SOAP : appContext=" + expectedAppContextId)); + } + + /** + * @keywords: jaspic_soap + * + * @testName: MessageInfo + * + * @assertion_ids: JASPIC:SPEC:35; JASPIC:SPEC:44; JASPIC:JAVADOC:5; + * JASPIC:SPEC:112; JASPIC:JAVADOC:9; JASPIC:JAVADOC:10; + * JASPIC:JAVADOC:11; JASPIC:JAVADOC:28; JASPIC:SPEC:23; + * JASPIC:SPEC:19; JASPIC:SPEC:36; JASPIC:SPEC:37; + * JASPIC:SPEC:43; JASPIC:SPEC:51; JASPIC:SPEC:113; + * JASPIC:SPEC:131; JASPIC:SPEC:132; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the messageInfo passed to secureRequest() + * validateRequest(), secureResponse() and validateResponse() + * contiains right values for getRequestMessage() and + * getResponseMessage() as per the spec. + * + * 3. clientSubject - a Subject that represents the source of + * the service request, or null. + * + * Description The MessageInfo argument used in any call made + * by the message processing runtime to secureRequest, + * validateResponse, validateRequest, or secureResponse must + * have been initialized such that the non-null objects + * returned by the getRequestMessage and getResponseMessage + * methods of the MessageInfo are an instanceof + * jakarta.xml.soap.SOAPMessage. + * + * + */ + @Test + public void MessageInfo() { + // verify whether the log contains required messages. + assertTrue( + "MessageInfo failed : " + "The request and response messages contains incorrect values", + logProcessor.verifyLogContains( + // "secureRequest : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "validateRequest : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "secureResponse : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "secureResponse : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage" + + + )); + + // "validateResponse : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + //"validateResponse : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage" + } + + /** + * @testName: ServerRuntimeCommonCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:JAVADOC:35; JASPIC:JAVADOC:36; + * JASPIC:JAVADOC:49; JASPIC:JAVADOC:51; JASPIC:JAVADOC:54; + * JASPIC:JAVADOC:63; JASPIC:JAVADOC:65; JASPIC:JAVADOC:68; + * JASPIC:JAVADOC:69; JASPIC:JAVADOC:71; JASPIC:JAVADOC:106 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether CallbackHandler for server runtime supports + * CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, + * TrustStoreCallback + * + * Description + * + * The CallbackHandler passed to the initialize method of an + * authentication module should support the following + * callbacks, and it must be possible to configure the runtime + * such that the CallbackHandler passed at module + * initialization module supports the following callbacks (in + * addition to any others required to be supported by the + * applicable internal profile): CertStoreCallback, + * PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * + */ + @Test + public void ServerRuntimeCommonCallbackSupport() { + // Verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "In SOAP : ServerRuntime CallbackHandler supports CertStoreCallback", + "In SOAP : ServerRuntime CallbackHandler supports PrivateKeyCallback", + "In SOAP : ServerRuntime CallbackHandler supports SecretKeyCallback", + "In SOAP : ServerRuntime CallbackHandler supports TrustStoreCallback")); + } + + /** + * @testName: ServerRuntimeCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:SPEC:149; JASPIC:JAVADOC:38; + * JASPIC:JAVADOC:39; JASPIC:JAVADOC:40; JASPIC:JAVADOC:42; + * JASPIC:JAVADOC:43; JASPIC:JAVADOC:44; JASPIC:JAVADOC:46; + * JASPIC:JAVADOC:30; JASPIC:JAVADOC:41; JASPIC:JAVADOC:45 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether CallbackHandler for server runtime supports + * CallerPrincipalCallback, GroupPrincipalCallback and + * PasswordValidationCallback + * + * Description + * + * The CallbackHandler passed to the + * ServerAuthModule.initialize must support the following + * callbacks, + * + * CallerPrincipalCallback, GroupPrincipalCallback, + * PasswordValidationCallback + * + * + */ + @Test + public void ServerRuntimeCallbackSupport() { + // Verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "In SOAP : ServerRuntime CallbackHandler supports CallerPrincipalCallback", + "In SOAP : ServerRuntime CallbackHandler supports GroupPrincipalCallback", + "In SOAP : ServerRuntime CallbackHandler supports PasswordValidationCallback")); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: OperationId + * + * @assertion_ids: JASPIC:SPEC:121; JASPIC:SPEC:125; JASPIC:JAVADOC:73 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the operationId is "sayHelloProtected" + * + * Description + * + * If getOperation returns a non-null operation identifier, + * then the operation identifier must be the String value + * corresponding to the operation name appearing in the + * service description (i.e., WSDL). + * + * When its getOperation method is called, any authentication + * context configuration object obtained for the SOAP layer, + * must attempt to derive the corresponding operation + * identifier value from the request message (within + * messageInfo). + * + * + */ + @Test + public void OperationId() { + assertTrue( + logProcessor.verifyLogContains( + "getAuthContextID() called for layer=SOAP shows AuthContextId=" + + "sayHelloProtected")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPAuthContext + * + * @assertion_ids: JASPIC:SPEC:125; JASPIC:SPEC:150; JASPIC:JAVADOC:5; + * JASPIC:JAVADOC:28; JASPIC:JAVADOC:73; JASPIC:JAVADOC:79 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the arguments(operation) passed to obtain + * getAuthContext is same as defined in Section 4.7.1 + * + * Description The authentication context identifier used in + * the call to getAuthContext must be equivalent to the value + * that would be acquired by calling getAuthContextID with the + * MessageInfo that will be used in the corresponding call to + * secureRequest (by a client runtime) or validateRequest (by + * a server runtime). + * + */ + @Test + public void ACPAuthContext() { + assertTrue( + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :" + expectedAppContextId, + "TSServerAuthConfig.getAuthContext: layer=SOAP" + " : appContext=" + expectedAppContextId + " operationId=sayHelloProtected")); + + +// String cArgs[] = { "service/HelloService", +// "http://" + hostname + ":" + portnum + "/Hello_web/Hello" }; +// +// // verify whether the log contains required messages. +// logProcessor.verifyLogContainsOneOfSubString(cArgs, +// "TSClientAuthConfig.getAuthContext: layer=SOAP" +// + " : appContext="); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPServerAuthConfig + * + * @assertion_ids: JASPIC:SPEC:150; JASPIC:JAVADOC:79; JASPIC:JAVADOC:94 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the arguments(layer and appcontextId) passed + * to obtain AuthConfigProvider is same as the arguments used + * in calling getServerAuthConfig. + * + * Description + * + * If a non-null AuthConfigProvider is returned (by the call + * to getConfigProvider), the messaging runtime must call + * getServerAuthConfig on the provider to obtain the + * authentication context configuration object pertaining to + * the application context at the layer. The layer and + * appContext arguments of the call to getServerAuthConfig + * must be the same as those used to acquire the provider. + * + * + */ + public void ACPServerAuthConfig() { + // verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :" + expectedAppContextId, + "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=SOAP : appContext=" + expectedAppContextId )); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SACRequestResponse + * + * @assertion_ids: JASPIC:SPEC:130; JASPIC:JAVADOC:13; JASPIC:JAVADOC:16; + * JASPIC:JAVADOC:17; JASPIC:JAVADOC:23; JASPIC:JAVADOC:26; + * JASPIC:JAVADOC:28; JASPIC:SPEC:155; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify whether for a non-null ServerAuthContext, + * validateRequest and secureResponse are called properly. + * + * Description + * + * If the server runtime obtained a non-null ServerAuthContext + * by using the operation identifier corresponding to the + * request message, then at point (2) in the message + * processing model, the runtime must call validateRequest on + * the ClientAuthContext, and at point (3) the runtime must + * call secureResponse on the ServerAuthContext. + * + * + */ + public void SACRequestResponse() { + // verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "TSServerAuthConfig.getAuthContext: returned non-null" + " ServerAuthContext for operationId=sayHelloProtected", + "TSServerAuthContext.validateRequest called", + "TSServerAuthContext.secureResponse called" )); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAppContextId + * + * @assertion_ids: JASPIC:SPEC:209 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify whether for the server side appilcation context + * Identifier is correctly used by the runtime. + * + * Description A server application context Identifier shall + * be the String value composed by concatenating a logical + * hostname a blank separator character, and the path + * component of the service endpoint URI corresponding to the + * webservice. + */ + @Test + public void ServerAppContextId() { + String args[] = { + logicalHostName + " /Hello_web/Hello" }; + + // verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=SOAP : appContext=")); + } + + + +} \ No newline at end of file diff --git a/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java new file mode 100644 index 0000000..a807597 --- /dev/null +++ b/tck/profile-spi/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java @@ -0,0 +1,606 @@ +package ee.jakarta.tck.authentication.test.basic; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.TSSV_ACF; +import static jakarta.security.auth.message.config.AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactoryForStandalone; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSRegistrationListener; +import ee.jakarta.tck.authentication.test.basic.servlet.CommonTests; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import java.security.Security; +import java.util.Collection; +import java.util.Iterator; +import java.util.logging.Logger; +import org.junit.Before; +import org.junit.Test; + +public class SoapUnitTest { + + Logger logger = Logger.getLogger(SoapUnitTest.class.getName()); + + private String logFileLocation = System.getProperty("log.file.location"); + private String providerConfigFileLocation = System.getProperty("provider.configuration.file"); + private String vendorACFClass = System.getProperty("vendor.authconfig.factory"); + private String soapAppContext = "localhost /Hello_web/Hello"; + + private transient ProviderConfigurationXMLFileProcessor configFileProcessor; + + private Collection providerConfigurationEntriesCollection; + + private CommonTests commonTests = new CommonTests(); + + @Before + public void setup() { + AuthConfigFactory.setFactory(null); + Security.setProperty(DEFAULT_FACTORY_SECURITY_PROPERTY, TSSV_ACF); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFGetFactory + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:335; JASPIC:SPEC:7; + * + * @test_Strategy: This s mainly concerned with testing the runtimes handling of ACF as follows: - get current (CTS) ACF + * - switch to use different (CTS) ACF - verify calls to ACF use the newer/expected ACF - restore original ACF + */ + @Test + public void ACFGetFactory() { + boolean passed = false; + try { + commonTests._ACF_getFactory(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: ACFSwitchFactorys + * + * @assertion_ids: + * + * + * @test_Strategy: This test does the following: - gets current (CTS) factory - sets the vendors ACF thus replacing the + * CTS ACF - verify ACF's were correctly set - reset factory back to the original CTS factory + * + * 1. Use the static setFactory method to set an ACF and this should always work. and use the getFactory to verify it + * worked. + * + * Description + * + */ + @Test + public void ACFSwitchFactorys() { + boolean passed = false; + try { + commonTests._ACFSwitchFactorys(vendorACFClass); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: testACFComesFromSecFile + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:330; + * + * @test_Strategy: This is calling a method on the server(actually servlet) side that will invoke getFactory() to verify + * a non-null facotry instance is returned. It will also verify that the authconfigprovider.factory security property is + * properly set/used. + * + */ + @Test + public void testACFComesFromSecFile() { + boolean passed = false; + try { + commonTests._testACFComesFromSecFile(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFPersistentRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:331; JASPIC:SPEC:332; JASPIC:SPEC:340; JASPIC:SPEC:341; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register another persistent ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP + * - try to re-register (persistently) standalone provider - verify 2nd attempt at added standalone provider REPLACED + * the first but it should NOT have added another nor failed. - also confirm that regID for standalone ACP stayed the + * same after 1st and 2nd attempt to register standalone ACP - verify that the 2nd re-registering of ACP replaced the + * ACP AND the description. - unregister standalone ACP and setFactory back to CTS default + * + */ + @Test + public void ACFPersistentRegisterOnlyOneACP() { + boolean passed = false; + try { + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, true); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFInMemoryRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:334; JASPIC:SPEC:342; JASPIC:SPEC:343; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register (in-memory) ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP - try + * to re-register (in-memory) standalone provider - verify 2nd attempt at added standalone provider REPLACED the first + * but it should NOT have added another nor failed. - also confirm that regID for standalone ACP stayed the same after + * 1st and 2nd attempt to register standalone ACP - verify that the 2nd re-registering of ACP replaced the ACP AND the + * description. - unregister standalone ACP and setFactory back to CTS default + * + */ + @Test + public void ACFInMemoryRegisterOnlyOneACP() { + boolean passed = false; + try { + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, false); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFUnregisterACP + * + * @assertion_ids: JASPIC:SPEC:344; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register (in-memory) ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP - + * unregister the in-memory ACP we just registered - verify removeRegistration() method call returned proper boolean - + * verify expected # of registry eentries - verify 2nd call to removeRegistration() with regId that was previously + * removed and should expect return val of false + * + */ + @Test + public void ACFUnregisterACP() { + boolean passed = false; + try { + commonTests._ACFUnregisterACP(logFileLocation, providerConfigFileLocation, vendorACFClass); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: ACFRemoveRegistrationWithBadId + * + * @assertion_ids: JASPIC:SPEC:345; + * + * + * @test_Strategy: This test verifies we get a return value of False when invoking ACF.removeRegistration(some_bad_id); + * + * 1. Use the static setFactory method to get an ACF and then attempt to invoke removeRegistration() with an invalid (ie + * non-existant) regId. This should return False (per javadoc). + * + * Description + * + */ + @Test + public void ACFRemoveRegistrationWithBadId() { + boolean passed = false; + try { + commonTests._ACFRemoveRegistrationWithBadId(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: getRegistrationContextId + * + * @assertion_ids: JASPIC:JAVADOC:77 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description This will use an appContext value that was used to register a provider, and it will see if it can use the + * AuthConfigFactory.RegistrationContext API to try and access the same appContext value that was used during the + * registration process. + * + */ + @Test + public void getRegistrationContextId() { + String appContext = "localhost /Hello_web/Hello"; + + // register providers in vendor factory + assertTrue(register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + + // verify we can access a given provider (any provider) appcontext id + boolean bVerified = false; + + AuthConfigFactory acf = null; + + try { + acf = AuthConfigFactory.getFactory(); + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + } + + assertNotNull( + "getRegistrationContextId failed : could not get acf", + acf); + + + String[] regIDs = acf.getRegistrationIDs(null); + for (int ii = 0; ii < regIDs.length; ii++) { + // loop through the ACF's registration ids + + if (regIDs[ii] != null) { + AuthConfigFactory.RegistrationContext acfReg; + acfReg = acf.getRegistrationContext(regIDs[ii]); + if (acfReg != null) { + logger.info("appContext = " + appContext); + logger.info("acfReg.getAppContext() = " + acfReg.getAppContext()); + logger.info("layer = " + acfReg.getMessageLayer()); + String str = acfReg.getAppContext(); + if ((str != null) && (str.equals(appContext))) { + // we found our provider info + logger.info("Found it : RegistrationID for our ACP=" + regIDs[ii]); + bVerified = true; + break; + } + } + } + } + + String msg = "Could not find appContext=" + appContext; + msg += " in the ACF's list of registration id info"; + + assertTrue(msg, bVerified); + + logger.info("TestSuite Providers registration successful"); + } + + /** + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactoryRegistration + * + * @assertion_ids: JASPIC:JAVADOC:80 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description + * + * + */ + @Test + public void AuthConfigFactoryRegistration() { + assertTrue( + register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + } + + public boolean register(String logFileLocation, String providerConfigFileLocation, String vendorACFClass) { + try { + printVerticalIndent(); + + // Get an instance of Vendor's AuthConfigFactory + AuthConfigFactory vendorFactory = getVendorAuthConfigFactory(vendorACFClass); + + // Set vendor's AuthConfigFactory + AuthConfigFactory.setFactory(vendorFactory); + + // Get system default AuthConfigFactory + AuthConfigFactory systemAuthConfigFactory = AuthConfigFactory.getFactory(); + + if (systemAuthConfigFactory != null) { + logger.info("Default AuthConfigFactory class name = " + systemAuthConfigFactory.getClass().getName()); + + } else { + logger.severe("Default AuthConfigFactory is null" + " can't register TestSuite Providers with null"); + return false; + } + + /** + * Read the ProviderConfiguration XML file This file contains the list of providers that needs to be loaded by the + * vendor's default AuthConfigFactory + */ + providerConfigurationEntriesCollection = readProviderConfigurationXMLFile(); + + ProviderConfigurationEntry pce = null; + + printVerticalIndent(); + Iterator iterator = providerConfigurationEntriesCollection.iterator(); + while (iterator.hasNext()) { + // obtain each ProviderConfigurationEntry and register it + // with vendor's default AuthConfigFactory + pce = iterator.next(); + + if (pce != null) { + logger.info("Registering Provider " + pce.getProviderClassName() + " ..."); + systemAuthConfigFactory.registerConfigProvider( + pce.getProviderClassName(), + pce.getProperties(), + pce.getMessageLayer(), + pce.getApplicationContextId(), + pce.getRegistrationDescription()); + + logger.info("Registration Successful"); + } + } + + printVerticalIndent(); + + // Check whether the providers are registered for the right message layer + // and appContext + // verifyRegistrations(acf); + + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory"; + msg = msg + "or ACF.setFactory(). Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + logger.info("Error Registering TestSuite AuthConfig Providers"); + e.printStackTrace(); + } + + return true; + + } + + /** + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactoryVerifyPersistence + * + * @assertion_ids: JASPIC:JAVADOC:75 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Load vendor's AuthConfigFactory and make sure the registered providers return properly for the right message layer + * and appContextId + * + * Note: We test the persistance behaviour for vendor's AuthConfigFactory by registering providers from a persisted + * file, then we verify the registrations went correctly. + * + * Description + * + * + */ + public void AuthConfigFactoryVerifyPersistence() { + try { + + // register providers in vendor factory + assertTrue(register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + + // Get system default AuthConfigFactory + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + + if (acf != null) { + logger.info("Default AuthConfigFactory class name = " + acf.getClass().getName()); + + assertTrue(verifyRegistrations(acf)); + + } else { + logger.severe("Default AuthConfigFactory is null" + " can't verify registrations for TestSuite Providers"); + } + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactorySetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:87; JASPIC:JAVADOC:80 + * + * @test_Strategy: 1. Use the static setFactory method to set an ACF and this should always work. + * + * Description This method uses the getFactory() method to get the current factory, then it uses the setFactory() to + * change the current ACF. This method then verifies that the ACF's were indeed changed. We know that the setFactory() + * works as it is used in the bootstrap process - but this is an additional level of testing that allows us to set the + * factory in a slightly different manner than the bootstrap (eg reading it out of the java.security file. + */ + @Test + public void AuthConfigFactorySetFactory() { + // Get current AuthConfigFactory + AuthConfigFactory currentAcf = AuthConfigFactory.getFactory(); + + assertNotNull( + "FAILURE - Could not get current AuthConfigFactory.", + currentAcf); + + String currentACFClass = currentAcf.getClass().getName(); + logger.info("currentACFClass = " + currentACFClass); + + // Set our ACF to a new/different AuthConfigFactory + TSAuthConfigFactoryForStandalone newAcf = new TSAuthConfigFactoryForStandalone(); + AuthConfigFactory.setFactory(newAcf); + String newACFClass = newAcf.getClass().getName(); + logger.info("newACFClass = " + newACFClass); + + // Verify our calls to getFactory() are getting the newly set factory + // instance and not the original ACF instance + AuthConfigFactory testAcf = AuthConfigFactory.getFactory(); + + assertNotNull( + "FAILURE - Could not get newly set AuthConfigFactory.", + testAcf); + + String newlySetACFClass = testAcf.getClass().getName(); + logger.info("newlySetACFClass = " + newlySetACFClass); + + assertEquals( + "FAILURE - our current ACF does not match our newly set ACF", + newACFClass, newlySetACFClass); + + logger.info("newlySetACFClass == newACFClass == " + newACFClass); + + // Restore original factory class + AuthConfigFactory.setFactory(currentAcf); + + logger.info("AuthConfigFactorySetFactory : PASSED"); + } + + + + private void printVerticalIndent() { + logger.info("**********************************************************"); + logger.info("\n"); + } + + /** + * This method instantiates and returns a AuthConfigFactory based on the specified className + */ + private AuthConfigFactory getVendorAuthConfigFactory(String className) { + AuthConfigFactory vFactory = null; + + if (className != null) { + try { + vFactory = (AuthConfigFactory) + Class.forName(className, true, Thread.currentThread() + .getContextClassLoader()) + .getDeclaredConstructor() + .newInstance(); + logger.info("Instantiated Vendor's AuthConfigFactory"); + + } catch (Exception e) { + logger.info("Error instantiating vendor's " + "AuthConfigFactory class :" + className); + e.printStackTrace(); + + } + } + + return vFactory; + } + + /* + * Read the provider configuration XML file and registers each provider with Vendor's default AuthConfigFactory + */ + private Collection readProviderConfigurationXMLFile() { + Collection providerConfigurationEntriesCollection = null; + + logger.info("Reading TestSuite Providers from :" + providerConfigFileLocation); + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + configFileProcessor = new ProviderConfigurationXMLFileProcessor(providerConfigFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + providerConfigurationEntriesCollection = configFileProcessor.getProviderConfigurationEntriesCollection(); + + logger.info("TestSuite Providers read successfully " + "from ProviderConfiguration.xml file"); + + return providerConfigurationEntriesCollection; + + } catch (Exception e) { + logger.info("Error loading Providers"); + e.printStackTrace(); + } + + return null; + } + + private boolean verifyRegistrations(AuthConfigFactory acf) { + logger.info("Verifying Provider Registrations ..."); + + try { + // Create a Registration listener + RegistrationListener rlis = new TSRegistrationListener(); + + // Get AuthConfigProvider for soap layer + AuthConfigProvider acp = acf.getConfigProvider(JASPICData.LAYER_SOAP, soapAppContext, rlis); + + if (acp != null) { + if (acp.getClass().getName().equals("com.sun.ts.tests.jaspic.tssv.config.TSAuthConfigProvider")) { + logger.info("TSAuthConfigProvider registered for" + " message layer=SOAP" + " and appContextId=" + soapAppContext); + } else { + logger.info("Wrong provider registerd for " + " message layer=SOAP" + " and appContextId=" + soapAppContext); + return false; + + } + + } else { + logger.info( + "Error : No AuthConfigprovider registerd for" + " message layer=SOAP" + " and appContextId=" + soapAppContext); + return false; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + + } +} diff --git a/tck/spi/common/pom.xml b/tck/spi/common/pom.xml new file mode 100644 index 0000000..456ff4d --- /dev/null +++ b/tck/spi/common/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + + org.eclipse.ee4j.tck.authentication + spi + 3.1.0-SNAPSHOT + + + spi.common + + Jakarta Authentication TCK - Profile SPI Common + + A lot of tests about the nitty gritty regarding the SPI per profile. + + + + + org.jakartaee + jaspic-common + 1.0-SNAPSHOT + + + jakarta.xml.ws + jakarta.xml.ws-api + 4.0.2 + + + diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/AuthDataCallbackHandler.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/AuthDataCallbackHandler.java new file mode 100644 index 0000000..68ae4ea --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/AuthDataCallbackHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; + +/** + * + * @author Raja Perumal + */ +public class AuthDataCallbackHandler implements CallbackHandler { + + private String user; + private String password; + + // Default constructor gets the user and password from the environment + // using system property j2eelogin.name and j2eelogin.password + public AuthDataCallbackHandler() { + user = System.getProperty("j2eelogin.name"); + password = System.getProperty("j2eelogin.password"); + } + + public AuthDataCallbackHandler(String usr, String pwd) { + user = usr; + password = pwd; + } + + @Override + public void handle(Callback[] callbacks) { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + NameCallback nc = (NameCallback) cb; + nc.setName(user); + } else if (cb instanceof PasswordCallback) { + PasswordCallback pc = (PasswordCallback) cb; + if (password != null) { + pc.setPassword(password.toCharArray()); + } else { + pc.setPassword(null); + } + } + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/CommonCallbackSupport.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/CommonCallbackSupport.java new file mode 100644 index 0000000..8347519 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/CommonCallbackSupport.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.callback.CertStoreCallback; +import jakarta.security.auth.message.callback.PrivateKeyCallback; +import jakarta.security.auth.message.callback.SecretKeyCallback; +import jakarta.security.auth.message.callback.TrustStoreCallback; +import java.io.IOException; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.CertStore; +import java.util.logging.Level; +import javax.crypto.SecretKey; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * + * @author Raja Perumal + */ +public class CommonCallbackSupport { + private static TSLogger logger = null; + private static CallbackHandler callbackHandler = null; + private static String profile = null; + private static String runtimeType = null; + + public CommonCallbackSupport(TSLogger tsLogger, CallbackHandler cbkHandler, String profile, String runtimeType) { + logger = tsLogger; + callbackHandler = cbkHandler; + this.profile = profile; + this.runtimeType = runtimeType; + } + + public boolean verify() { + try { + CertStoreCallbackSupport(); + PrivateKeyCallbackSupport(); + SecretKeyCallbackSupport(); + TrustStoreCallbackSupport(); + return true; + + } catch (Exception e) { + return false; + } + } + + private void CertStoreCallbackSupport() { + if (callbackHandler != null) { + try { + CertStoreCallback certStoreCallback = new CertStoreCallback(); + Callback[] callbacks = new Callback[] { certStoreCallback }; + + callbackHandler.handle(callbacks); + CertStore certStore = certStoreCallback.getCertStore(); + + if (certStore != null) { + logMsg("CertStore type =" + certStore.getType()); + } + logMsg("CallbackHandler supports CertStoreCallback"); + + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support CertStoreCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support CertStoreCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + + } + + } + + private void PrivateKeyCallbackSupport() { + if (callbackHandler != null) { + try { + + PrivateKeyCallback.AliasRequest aliasRequest = new PrivateKeyCallback.AliasRequest("s1as"); + + PrivateKeyCallback privateKeyCallback = new PrivateKeyCallback(aliasRequest); + + Callback[] callbacks = new Callback[] { privateKeyCallback }; + + callbackHandler.handle(callbacks); + + PrivateKey privateKey = privateKeyCallback.getKey(); + + if (privateKey != null) { + logMsg("Private Key for s1as =" + privateKey.getAlgorithm()); + } + logMsg("CallbackHandler supports PrivateKeyCallback"); + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support PrivateKeyCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support PrivateKeyCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + + } + } + + private void SecretKeyCallbackSupport() { + if (callbackHandler != null) { + try { + + SecretKeyCallback.AliasRequest aliasRequest = new SecretKeyCallback.AliasRequest("s1as"); + + SecretKeyCallback secretKeyCallback = new SecretKeyCallback(aliasRequest); + + Callback[] callbacks = new Callback[] { secretKeyCallback }; + + callbackHandler.handle(callbacks); + + SecretKey secretKey = secretKeyCallback.getKey(); + + if (secretKey != null) { + logMsg("Secret Key for s1as =" + secretKey.getAlgorithm()); + } + logMsg("CallbackHandler supports SecretKeyCallback"); + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support secretKeyCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support secretKeyCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + + } + + } + + private void TrustStoreCallbackSupport() { + if (callbackHandler != null) { + try { + TrustStoreCallback trustStoreCallback = new TrustStoreCallback(); + Callback[] callbacks = new Callback[] { trustStoreCallback }; + + callbackHandler.handle(callbacks); + KeyStore trustStore = trustStoreCallback.getTrustStore(); + + if (trustStore != null) { + logMsg("TrustStore type =" + trustStore.getType()); + } + logMsg("CallbackHandler supports TrustStoreCallback"); + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support TrustStoreCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support TrustStoreCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + } + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, "In " + profile + " : " + runtimeType + " " + str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationEntry.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationEntry.java new file mode 100644 index 0000000..7d339a9 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationEntry.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * + * @author Raja Perumal + */ +public class ProviderConfigurationEntry implements Serializable { + + private static final long serialVersionUID = 249772730727896379L; + + private String providerClassName; + private Map properties; + private String messageLayer; + private String applicationContextId; + private String registrationDescription; + + /** Creates a new instance of ProviderConfigurationEntry */ + public ProviderConfigurationEntry(Node providerConfigEntryNode) throws Exception { + Node childNode; + String nodeName; + + // make sure the nodename is provider-config-entry + if (!providerConfigEntryNode.getNodeName().equals("provider-config-entry")) { + throw new Exception("Unexpected tag :" + providerConfigEntryNode.getNodeName()); + } + NodeList nodes = providerConfigEntryNode.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + + childNode = nodes.item(i); + nodeName = childNode.getNodeName(); + + // Skip empty text node processing + if (nodeName.equals("#text")) + continue; + + if (nodeName.equals("provider-class")) { + providerClassName = getText(childNode); + + } else if (nodeName.equals("properties")) { + properties = loadProperties(childNode); + + } else if (nodeName.equals("message-layer")) { + messageLayer = getText(childNode); + + } else if (nodeName.equals("app-context-id")) { + applicationContextId = getText(childNode); + + } else if (nodeName.equals("reg-description")) { + registrationDescription = getText(childNode); + } + } + + } + + // This method loads a given Properties node such as the one shown below + // and stores the values into a properties object called "properties" + // + // true + // USER_NAME_PASSWORD + // + private static Map loadProperties(Node node) { + Node topLevelChildNode = null; + String topLevelNodeName; + String key = null; + String value = null; + NamedNodeMap namedNodeMap = null; + Properties nodeProperties = new Properties(); + + NodeList nodes = node.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + if (topLevelNodeName.equals("entry")) { + namedNodeMap = topLevelChildNode.getAttributes(); + Node tempKeyNode = namedNodeMap.getNamedItem("key"); + key = tempKeyNode.getNodeValue(); + value = topLevelChildNode.getFirstChild().getNodeValue(); + nodeProperties.put(key, value); + } + } + + HashMap map = new HashMap<>(); + nodeProperties.stringPropertyNames().forEach((tmpkey) -> map.put(tmpkey, nodeProperties.getProperty(tmpkey))); + return map; + } + + public String getProviderClassName() { + return providerClassName; + } + + public String getMessageLayer() { + return messageLayer; + } + + public String getApplicationContextId() { + return applicationContextId; + } + + public String getRegistrationDescription() { + return registrationDescription; + } + + public Map getProperties() { + return properties; + } + + public String getText(Node textNode) { + String result = ""; + NodeList nodes = textNode.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node.getNodeType() == Node.TEXT_NODE) { + result = node.getNodeValue(); + break; + } + } + + if (result != null) + result = result.trim(); + + return result; + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationXMLFileProcessor.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationXMLFileProcessor.java new file mode 100644 index 0000000..b0c6d3c --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ProviderConfigurationXMLFileProcessor.java @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * + * @author Raja Perumal + */ +public class ProviderConfigurationXMLFileProcessor { + + private static Collection providerConfigurationEntriesCollection = new Vector(); + + private static Document document = null; + private static File providerConfigFile = null; + + /** Creates a new instance of ProviderConfigurationXMLFileReader */ + public ProviderConfigurationXMLFileProcessor(String fileName) throws Exception { + + try { + providerConfigurationEntriesCollection.clear(); // XXXX: + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + documentBuilder.setEntityResolver(new DTDResolver()); + + if (fileName != null) + providerConfigFile = new File(fileName); + + if (!providerConfigFile.exists()) { + throw new Exception("Provider Config File : " + fileName + " does not exists"); + } else { + + FileInputStream fis = new FileInputStream(providerConfigFile); + + // Parse of the content of the file into Document + document = documentBuilder.parse(fis); + + // get the root element "provider-config" + Element rootElement = document.getDocumentElement(); + + // get the childNodes inside the rootElement + // The ChildNodes are instances of "provider-config-entry" elements + NodeList nodes = rootElement.getChildNodes(); + + // For each "provider-config-entry" element load all the properties + // and add the ProviderConfigurationEntry into the collection + setProviderConfigEntryCollection(nodes); + } + + } catch (ParserConfigurationException pce) { + throw new Exception("PaserConfigurationException :" + pce.getMessage()); + } catch (SAXException se) { + throw new Exception("SAXException :" + se.getMessage()); + } catch (IOException ioe) { + throw new Exception("IOException :" + ioe.getMessage()); + + } catch (SecurityException se) { + throw new Exception("SecurityException :" + se.getMessage()); + } + } + + private void setProviderConfigEntryCollection(NodeList nodes) throws Exception { + + Node providerConfigEntryNode; + NodeList providerConfigEntryNodeChildren; + + for (int i = 0; i < nodes.getLength(); i++) { + // Take the first node + providerConfigEntryNode = nodes.item(i); + providerConfigEntryNodeChildren = providerConfigEntryNode.getChildNodes(); + + // Skip empty text node processing + if (providerConfigEntryNode.getNodeName().equals("#text")) + continue; + + ProviderConfigurationEntry pce = new ProviderConfigurationEntry(providerConfigEntryNode); + providerConfigurationEntriesCollection.add(pce); + } + } + + public Collection getProviderConfigurationEntriesCollection() { + return providerConfigurationEntriesCollection; + } + + // This method creates a new provider-config-entry node and adds it to the + // root (provider-config) element. + // On successful insertion of provider-config-entry this method returns true + public static boolean addProviderConfigEntry(String className, Map props, String messageLayer, String appContextId, String description) { + boolean result = false; + + Node providerConfigEntry = createProviderConfigEntry(className, props, messageLayer, appContextId, description); + + boolean alreadyExists = checkIfAlreadyPresent(providerConfigEntry); + + // If the currentNode doesn't exists in the configuration file then + // add the current node + if (!alreadyExists) { + + // get the root element "provider-config" + Element rootElement = document.getDocumentElement(); + try { + rootElement.appendChild(providerConfigEntry); + updateProviderConfigurationXMLFile(); + result = true; + } catch (DOMException dome) { + result = false; + } + } + + return result; + } + + // This method creates a new provider-config-entry node + // + public static Node createProviderConfigEntry(String className, Map props, String messageLayer, String appContextId, String description) { + + Element providerConfigEntry = null; + + // get the root element "provider-config" + Element rootElement = document.getDocumentElement(); + + // create provider-config-entry + providerConfigEntry = (Element) document.createElement("provider-config-entry"); + + if (className != null) { + // create provider-class + Element providerClassEntry = (Element) document.createElement("provider-class"); + Text classNameText = document.createTextNode(className); + providerClassEntry.appendChild(classNameText); + + // Add provider-class to provider-config-entry + providerConfigEntry.appendChild(providerClassEntry); + } + + if (props != null) { + // create properties + Element propertiesNode = (Element) document.createElement("properties"); + + // Iterate through the map of properties(props) and add all the + // entries + Set entries = props.entrySet(); + Iterator iterator = entries.iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry) iterator.next(); + Element entryNode = (Element) document.createElement("entry"); + entryNode.setAttribute("key", entry.getKey().toString()); + Text keyValueText = document.createTextNode(entry.getValue().toString()); + entryNode.appendChild(keyValueText); + propertiesNode.appendChild(entryNode); + // System.out.println(entry.getKey() + " : " + // + entry.getValue()); + } + providerConfigEntry.appendChild(propertiesNode); + } + + if (messageLayer != null) { + // create message-layer + Element messageLayerEntry = (Element) document.createElement("message-layer"); + Text messageLayerText = document.createTextNode(messageLayer); + messageLayerEntry.appendChild(messageLayerText); + + // Add message-layer to provider-config-entry + providerConfigEntry.appendChild(messageLayerEntry); + } + + if (appContextId != null) { + // create app-context-id + Element appContextIdEntry = (Element) document.createElement("app-context-id"); + Text appContextIdText = document.createTextNode(appContextId); + appContextIdEntry.appendChild(appContextIdText); + + // Add app-context-id to provider-config-entry + providerConfigEntry.appendChild(appContextIdEntry); + } + + if (description != null) { + // create reg-description + Element regDescriptionEntry = (Element) document.createElement("reg-description"); + Text regDescriptionText = document.createTextNode(description); + regDescriptionEntry.appendChild(regDescriptionText); + + // Add provider-class to provider-config-entry + providerConfigEntry.appendChild(regDescriptionEntry); + } + + return providerConfigEntry; + } + + // This method uses the given string (className, messageLayer, + // appContextId and description) and compares them with the contents of + // provider-config-entry nodes, if the given strings matches for a given node + // then that node will be deleted. This method return false if the given + // strings doesn't match with any of the provider-config-entry nodes. + public static boolean deleteProviderConfigEntry(String className, String messageLayer, String appContextId, String description) { + boolean result = false; + + Node providerConfigEntry = createProviderConfigEntry(className, null, messageLayer, appContextId, description); + + boolean alreadyExists = checkIfAlreadyPresent(providerConfigEntry); + + // get the root element "provider-config" + Element rootElement = document.getDocumentElement(); + + if (alreadyExists) { + try { + rootElement.removeChild(providerConfigEntry); + updateProviderConfigurationXMLFile(); + result = true; + } catch (DOMException dome) { + result = false; + } + + } + + return result; + } + + // This method uses the given string (className, messageLayer, + // appContextId and description) and compares them with the contents of + // provider-config-entry nodes, if the all strings matches for a given node + // then this method returns the corresponding provider-config-entry node, + // if the given strings doesn't match with any of the provider-config-entry + // node then this method returns null + public static boolean checkIfAlreadyPresent(Node node) { + + Node topLevelChildNode = null; + String topLevelNodeName; + boolean result = false; + + // get the root element "provider-config" + Element rootElement = document.getDocumentElement(); + + NodeList nodes = rootElement.getChildNodes(); + + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + // Check whether the given node is same as the current + // provider-config-entry node + if (topLevelNodeName.equals("provider-config-entry")) { + topLevelChildNode.normalize(); + node.normalize(); + + // printNode(topLevelChildNode, i ); + // printNode(node, i); + + if (compareNode(topLevelChildNode, node)) { + return true; + } + } + } + + return result; + } + + private static boolean compareNode(Node source, Node target) { + + NodeList topLevelChildren; + Node childNode; + String nodeName; + + boolean classNameMatch = false; + boolean propertiesMatch = true; + boolean messageLayerMatch = false; + boolean appContextIdMatch = false; + boolean descriptionMatch = false; + + String className = null; + String messageLayer = null; + String appContextId = null; + Map properties = null; + String description = null; + + String targetClassName = null; + String targetMessageLayer = null; + String targetAppContextId = null; + String targetDescription = null; + + // get the root element "provider-config" + + // Process Source node and get className, messageLayer, + // properties, appContextId and description values + topLevelChildren = source.getChildNodes(); + for (int j = 0; j < topLevelChildren.getLength(); j++) { + + childNode = topLevelChildren.item(j); + nodeName = childNode.getNodeName(); + + // Skip empty text node processing + if (nodeName.equals("#text")) + continue; + + if (nodeName.equals("provider-class")) { + className = childNode.getFirstChild().getNodeValue().trim(); + + } + if (nodeName.equals("properties")) { + properties = getPropertiesMap(childNode); + + } else if (nodeName.equals("message-layer")) { + messageLayer = childNode.getFirstChild().getNodeValue().trim(); + + } else if (nodeName.equals("app-context-id")) { + appContextId = childNode.getFirstChild().getNodeValue().trim(); + + } else if (nodeName.equals("reg-description")) { + description = childNode.getFirstChild().getNodeValue().trim(); + } + } + + // Process target node and get className, messageLayer, + // properties, appContextId and description values + topLevelChildren = target.getChildNodes(); + for (int j = 0; j < topLevelChildren.getLength(); j++) { + + childNode = topLevelChildren.item(j); + nodeName = childNode.getNodeName(); + + // Skip empty text node processing + if (nodeName.equals("#text")) + continue; + + if (nodeName.equals("provider-class")) { + targetClassName = childNode.getFirstChild().getNodeValue().trim(); + + } + if (nodeName.equals("properties")) { + // targetProperties=getPropertiesMap(childNode); + // propertiesMatch = matchProperties(childNode, properties); + + } else if (nodeName.equals("message-layer")) { + targetMessageLayer = childNode.getFirstChild().getNodeValue().trim(); + + } else if (nodeName.equals("app-context-id")) { + targetAppContextId = childNode.getFirstChild().getNodeValue().trim(); + + } else if (nodeName.equals("reg-description")) { + targetDescription = childNode.getFirstChild().getNodeValue().trim(); + } + } + + if (stringCompare(className, targetClassName)) { + classNameMatch = true; + } + if (stringCompare(messageLayer, targetMessageLayer)) { + messageLayerMatch = true; + } + if (stringCompare(appContextId, targetAppContextId)) { + appContextIdMatch = true; + } + if (stringCompare(description, targetDescription)) { + descriptionMatch = true; + } + + if (classNameMatch && propertiesMatch && messageLayerMatch && appContextIdMatch && descriptionMatch) { + // if both node values are same then return true; + return true; + } + + return false; + } + + private static boolean stringCompare(String source, String target) { + boolean result = false; + + if ((source == null) && (target == null)) { + result = true; + } else if ((source != null) && target != null) { + if (source.equals(target)) { + result = true; + } + } + return result; + } + + // This method reads a Properties node such as the one shown below + // and returns properties Map from its values + // + // + // true + // USER_NAME_PASSWORD + // + private static Map getPropertiesMap(Node node) { + Node topLevelChildNode = null; + String topLevelNodeName; + String key = null; + String value = null; + NamedNodeMap namedNodeMap = null; + Map nodeProperties = new Properties(); + + NodeList nodes = node.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + if (topLevelNodeName.equals("entry")) { + namedNodeMap = topLevelChildNode.getAttributes(); + Node tempKeyNode = namedNodeMap.getNamedItem("key"); + key = tempKeyNode.getNodeValue(); + value = topLevelChildNode.getFirstChild().getNodeValue(); + nodeProperties.put(key, value); + } + + } + + return nodeProperties; + } + + // This method reads a Properties node such as the one shown below + // and constructs a properties Map from its values and compares that map with + // the passed verifyProperties Map, if both are equal this + // method returns true else false + // + // + // true + // USER_NAME_PASSWORD + // + private static boolean matchProperties(Node node, Map verifyProperties) { + Node topLevelChildNode = null; + String topLevelNodeName; + String key = null; + String value = null; + NamedNodeMap namedNodeMap = null; + Map nodeProperties = new Properties(); + boolean result = false; + + NodeList nodes = node.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + if (topLevelNodeName.equals("entry")) { + namedNodeMap = topLevelChildNode.getAttributes(); + Node tempKeyNode = namedNodeMap.getNamedItem("key"); + key = tempKeyNode.getNodeValue(); + value = topLevelChildNode.getFirstChild().getNodeValue(); + nodeProperties.put(key, value); + } + + } + if (nodeProperties.equals(verifyProperties)) + result = true; + + return result; + } + + private static void updateProviderConfigurationXMLFile() { + + // Style Sheet to indent a given XML file + String styleSheet = "" + + " " + " " + + " " + " " + " " + " " + + " " + ""; + + try { + + StreamSource styleSource = new StreamSource(new ByteArrayInputStream(styleSheet.getBytes())); + + // Use a Transformer for output + TransformerFactory tFactory = TransformerFactory.newInstance(); + + // Apply transformation specified by the stylesheet to indent the + // generated XML file + Transformer transformer = tFactory.newTransformer(styleSource); + + // If indentation affects performance, then we can also use + // simple transformation as shown below + // Transformer transformer = tFactory.newFtransformer(); + + DOMSource source = new DOMSource(document); + + // Get the DOCTYPE + String systemValue = (new File(document.getDoctype().getSystemId())).getName(); + + // Delete the original ProviderConfiguration file + if (providerConfigFile.exists()) { + providerConfigFile.delete(); + providerConfigFile.createNewFile(); + } + + FileOutputStream fos = new FileOutputStream(providerConfigFile); + + // StreamResult result = new StreamResult(System.out); + StreamResult result = new StreamResult(fos); + + // Add Doctype to the output xml file + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemValue); + + // Indent the XML file + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + + // set the output type as xml + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + + // Do identity transformation into the result stream + transformer.transform(source, result); + + fos.close(); + + } catch (TransformerConfigurationException tce) { + // Error generated by the parser + System.out.println("\n** Transformer Factory error"); + System.out.println(" " + tce.getMessage()); + + // Use the contained exception, if any + Throwable x = tce; + if (tce.getException() != null) + x = tce.getException(); + x.printStackTrace(); + + } catch (TransformerException te) { + // Error generated by the parser + System.out.println("\n** Transformation error"); + System.out.println(" " + te.getMessage()); + + // Use the contained exception, if any + Throwable x = te; + if (te.getException() != null) + x = te.getException(); + x.printStackTrace(); + + } catch (IOException ioe) { + // I/O error + ioe.printStackTrace(); + } + + } + + private static void printNodes(NodeList nodes) { + + Node topLevelChildNode; + String topLevelNodeName; + + int nodeCount = 1; + + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + printNode(topLevelChildNode, nodeCount++); + + } + } + + private static void printNode(Node node, int index) { + Node childNode; + String nodeName; + NodeList topLevelChildren; + + System.out.println(" "); + topLevelChildren = node.getChildNodes(); + + for (int j = 0; j < topLevelChildren.getLength(); j++) { + + childNode = topLevelChildren.item(j); + nodeName = childNode.getNodeName(); + + // Skip empty text node processing + if (nodeName.equals("#text")) + continue; + + if (nodeName.equals("provider-class")) { + System.out.println(index + " ) " + "provider-class = " + getText(childNode)); + + } else if (nodeName.equals("properties")) { + printProperties(childNode); + + } else if (nodeName.equals("message-layer")) { + System.out.println(index + " ) " + "message-layer = " + getText(childNode)); + + } else if (nodeName.equals("app-context-id")) { + System.out.println(index + " ) " + "app-context-id = " + getText(childNode)); + + } else if (nodeName.equals("reg-description")) { + System.out.println(index + " ) " + "reg-description = " + getText(childNode)); + } + } + System.out.println("-----------------"); + + } + + // This method prints a Properties node such as the one shown below + // + // + // true + // USER_NAME_PASSWORD + // + private static void printProperties(Node node) { + Node topLevelChildNode = null; + String topLevelNodeName; + String key = null; + String value = null; + NamedNodeMap namedNodeMap = null; + Properties nodeProperties = new Properties(); + + NodeList nodes = node.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + + topLevelChildNode = nodes.item(i); + topLevelNodeName = topLevelChildNode.getNodeName(); + + // Skip empty text node processing + if (topLevelNodeName.equals("#text")) + continue; + + if (topLevelNodeName.equals("entry")) { + namedNodeMap = topLevelChildNode.getAttributes(); + Node tempKeyNode = namedNodeMap.getNamedItem("key"); + key = tempKeyNode.getNodeValue(); + value = topLevelChildNode.getFirstChild().getNodeValue(); + nodeProperties.put(key, value); + } + } + + if ((nodeProperties != null) && (!nodeProperties.isEmpty())) { + nodeProperties.list(System.out); + } else { + System.out.println("No Properties to list"); + } + } + + private static String getText(Node textNode) { + String result = ""; + NodeList nodes = textNode.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node.getNodeType() == Node.TEXT_NODE) { + result = node.getNodeValue(); + break; + } + } + if (result != null) + result = result.trim(); + return result; + } + + public class DTDResolver implements EntityResolver { + public InputSource resolveEntity(String publicID, String systemId) throws SAXException { + String providerConfigurationFile = null; + String providerConfigurationFileLocation = null; + int indexof = 0; + + // Obtain the full path for the location of ProviderConfiguration.xml file + // From this value identify the directory of this file using indexOf() + providerConfigurationFile = System.getProperty("provider.configuration.file"); + if (providerConfigurationFile != null) { + + indexof = providerConfigurationFile.indexOf("ProviderConfiguration.xml"); + if (indexof > 0) { + // Get the directory location of ProviderConfiguration.xml file + providerConfigurationFileLocation = providerConfigurationFile.substring(0, indexof); + } + + if (systemId.contains("provider-configuration.xsd")) { + // location of schema is at /lib/schemas(from ts.jte) + String schemaLocation = System.getProperty("schema.file.location"); + return new InputSource(schemaLocation + File.separator + "provider-configuration.xsd"); + } + } + // if there is no match + return null; + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java new file mode 100644 index 0000000..21ce839 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/ServerCallbackSupport.java @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import ee.jakarta.tck.authentication.test.basic.sam.util.BASE64Decoder; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.callback.CallerPrincipalCallback; +import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.callback.PasswordValidationCallback; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * + * @author Raja Perumal + */ +public class ServerCallbackSupport { + + private static TSLogger logger = null; + private static CallbackHandler callbackHandler = null; + private static String profile = null; + private static final String runtimeType = "ServerRuntime"; + private static MessageInfo messageInfo = null; + private static Subject clientSubject = null; + private static Subject serverSubject = null; + + // user corresponds to ts.jte user property (e.g. "j2ee") + private static String user = System.getProperty("j2eelogin.name"); + + // password corresponds to ts.jte password property (e.g. "j2ee") + private static String passwd = System.getProperty("j2eelogin.password"); + + /** Creates a new instance of ServerCallbackSupport */ + public ServerCallbackSupport(TSLogger tsLogger, CallbackHandler cbkHandler, String profile) { + logger = tsLogger; + callbackHandler = cbkHandler; + this.profile = profile; + } + + public ServerCallbackSupport(TSLogger tsLogger, CallbackHandler cbkHandler, String profile, MessageInfo msgInfo, Subject clientSubj, Subject serverSubj) { + logger = tsLogger; + callbackHandler = cbkHandler; + this.profile = profile; + this.messageInfo = msgInfo; + this.clientSubject = clientSubj; + this.serverSubject = serverSubj; + } + + public boolean verify() { + try { + CallerPrincipalCallbackSupport(); + GroupPrincipalCallbackSupport(); + PasswordValidationCallbackSupport(); + return true; + } catch (Exception e) { + return false; + } + } + + public boolean verifyCPCCallback() { + boolean bval = CallerPrincipalCallbackSupport(); + logMsg("verifyCPCCallback returning " + Boolean.toString(bval)); + return bval; + } + + public boolean verifyGPCCallback() { + boolean bval = GroupPrincipalCallbackSupport(); + logMsg("verifyGPCCallback returning " + Boolean.toString(bval)); + return bval; + } + + public boolean verifyPVCCallback() { + boolean bval = PasswordValidationCallbackSupport(); + logMsg("verifyPVCCallback returning " + Boolean.toString(bval)); + return bval; + } + + private boolean CallerPrincipalCallbackSupport() { + boolean bval = false; + HttpServletRequest request = null; + + try { + request = (HttpServletRequest) messageInfo.getRequestMessage(); + } catch (Exception ex) { + } + + if (callbackHandler != null) { + try { + // note: we should be able to have a subject that has NO + // principals for the case of optional authen. Which means + // we should not have to explicitly set the principal here. + // however, for the case of mandatory authen, we will want + // to create a CPC using a username as opposed to a null principal. + + CallerPrincipalCallback callerPrincipalCallback = null; + if (profile.equals(JASPICData.LAYER_SERVLET)) { + Principal principal = null; + if (messageInfo != null) { + HttpServletRequest req = (HttpServletRequest) messageInfo.getRequestMessage(); + String username = getServletUsername(req); + String principalName = getPrincipalNameFromSubject(clientSubject); + String nameToLog = null; + + // better to call cbh with a null principal when the policy is Not + // mandatory + // and with a legitimate principal when the policy is mandatory + // (unless testing send-failure or send-continue - which we are + // not!) + boolean bIsMandatory = isServletAuthMandatory(messageInfo); + if (bIsMandatory) { + logMsg("CallerPrincipalCallbackSupport() Authentication mandatory"); + if (username != null) { + logMsg("CallerPrincipalCallbackSupport() auth mandatory, username != null"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, username); + nameToLog = username; + } else if (principalName != null) { + logMsg("CallerPrincipalCallbackSupport() auth mandatory, principalName != null"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, principalName); + nameToLog = principalName; + } else { + logMsg("CallerPrincipalCallbackSupport() auth mandatory, username and principalName both == null"); + } + } else { + logMsg("CallerPrincipalCallbackSupport() Authentication NOT mandatory"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, (Principal) null); + } + + // now for some simple invocations to ensure we can call the API's + // these lines serve no other purpose than to validate we can + // invoke the api's in order to satisfy the javadoc assertions of: + // JSR-196:JAVADOC:32, JSR-196:JAVADOC:33, JSR-196:JAVADOC:34 + String cpcbkName = callerPrincipalCallback.getName(); + Principal cpcbkPrin = callerPrincipalCallback.getPrincipal(); + Subject cpcbkSubj = callerPrincipalCallback.getSubject(); + + String msg = "CallerPrincipalCallback called for profile=" + profile; + if (request != null) { + String servletPath = request.getContextPath() + request.getServletPath(); + msg += " for servletPath=" + servletPath; + } else { + msg += " messageInfo contained null request"; + } + logMsg(msg); + + // this helps test JASPIC:SPEC:103 + if (clientSubject == null) { + msg += " subject=null"; + } else { + msg += " subject=non-null"; + } + msg += " principal set = " + nameToLog; + logMsg(msg); + + } else { + // uses a null principal + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, principal); + } + } else { + // should not get into here. + logMsg("WARNING: ServerCallbackSupport.CallerPrincipalCallbackSupport() - profile != servlet."); + Subject subject = new Subject(); + callerPrincipalCallback = new CallerPrincipalCallback(subject, (Principal) null); + } + + Callback[] callbacks = new Callback[] { callerPrincipalCallback }; + + callbackHandler.handle(callbacks); + + logMsg("CallbackHandler supports CallerPrincipalCallback"); + bval = true; // if here assume successful authen + + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support CallerPrincipalCallback :" + usce.getMessage()); + usce.printStackTrace(); + + } catch (Exception ex) { + // failed CPC authentication for unknown reason + String servletPath = ""; + if (request != null) { + servletPath = request.getContextPath() + request.getServletPath(); + } else { + servletPath = "WARNING: can't determine servletpath"; + } + logMsg("CPC Exception failure for servletPath=" + servletPath); + ex.printStackTrace(); + } + + } else { + String msg = "CallerPrincipalCallback has a null callbackHandler"; + msg += " profile=" + profile; + if (profile.equals(JASPICData.LAYER_SERVLET) && (messageInfo != null)) { + // HttpServletRequest request = + // (HttpServletRequest)messageInfo.getRequestMessage(); + if (request != null) { + String servletPath = request.getContextPath() + request.getServletPath(); + msg += " for servletPath=" + servletPath; + } else { + msg += " messageInfo contained null request"; + } + logMsg(msg); + + if (clientSubject == null) { + msg += " subject=null"; + } else { + msg += " subject=non-null"; + } + String principalConcatString = getPrincipalNameFromSubject(clientSubject); + msg += " principal set = " + principalConcatString; + + } + logMsg(msg); + } + + return bval; + } + + /* + * This is a convenience method. It is used to help pull out the username from the request. This method will only pull + * out a username if there was a client side servlet request made where Basic auth was used. This will only succeed if: + * 1. We have a ServletRequest that was populated correctly 2. The client side used BASE64Encoder() to encode user/pwd + * info 3. The user/pwd info were encoded in a format similar to the the following: String authData = + * username+":"+password BASE64Encode.encode(authData.getBytes()) + * + * This returns just the decoded username. + * + */ + private String getServletUsername(HttpServletRequest req) { + String username = null; + String authorization = req.getHeader("authorization"); + BASE64Decoder decoder = new BASE64Decoder(); + + if ((authorization != null) && (authorization.startsWith("Basic "))) { + try { + String authStr = authorization.substring(6).trim(); + String value = new String(decoder.decodeBuffer(authStr)); + logMsg("decoded (request) authorization string of: " + value); + + // at this point value should be in the form of : + if (value != null) { + username = value.substring(0, value.indexOf(":")); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + return username; + } + + /* + * Convenience method to get our context info. (This currently only works for servlet profile since thats all we need it + * for at this point.) + * + */ + private String getServletContext(MessageInfo mInfo, String profile) { + String sContext = ""; + if (profile.equals(JASPICData.LAYER_SERVLET) && (mInfo != null)) { + HttpServletRequest req = (HttpServletRequest) mInfo.getRequestMessage(); + if (req != null) { + sContext = req.getContextPath() + req.getServletPath(); + } + } + + logger.log(Level.INFO, "getServletContext() returning " + sContext); + + return sContext; + } + + /* + * This is a convenience method that is used to determine if Authentication is mandatory. Based on the answer, there are + * certain requirements that will need to be met wrt setting of principals. + */ + private boolean isServletAuthMandatory(MessageInfo msgInfo) { + boolean bval = false; + Map map = msgInfo.getMap(); + + // lets pull out some context info that we can use to help uniquely + // identify the source of this request + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + String servletName = request.getServletPath(); + + // see assertion JASPI:SPEC:306 for details on this + // jsr-196 states the following key must exist for servlet profile + String strKey = "jakarta.security.auth.message.MessagePolicy.isMandatory"; + String msg; + if (map != null) { + String keyVal = (String) map.get(strKey); + msg = "isServletAuthMandatory() called with attrs: "; + msg += " layer=" + JASPICData.LAYER_SERVLET; + msg += " servletName=" + servletName; + msg += " key=" + strKey; + + if (keyVal == null) { + msg += " value=NULL"; + bval = false; // assume false if we cant determine + } else if (Boolean.valueOf(keyVal).booleanValue() == true) { + msg += " value=Valid"; + bval = true; + } else { + // assume false + msg += " value=false"; + bval = false; + } + logger.log(Level.FINE, msg); + } else { + msg = "FAILURE: No map in MessageInfo thus no key=" + strKey; + logger.log(Level.SEVERE, msg); + } + + return bval; + } + + public String getPrincipalNameFromSubject(Subject sub) { + Principal principal = null; + + if (sub == null) { + return null; + } + + String concatPrincipalName = ""; + Set principalSet = sub.getPrincipals(); + + Iterator iterator = principalSet.iterator(); + while (iterator.hasNext()) { + principal = (Principal) iterator.next(); + concatPrincipalName += principal.getName(); + } + + return concatPrincipalName; + } + + private boolean GroupPrincipalCallbackSupport() { + boolean bval = false; + ; + boolean isAuthMandatory = false; + String strServletContext = ""; + String principalName = ""; + + if (callbackHandler != null) { + try { + // note: we should be able to have a subject that has NO + // principals for the case of optional authen. Which means + // we should not have to explicitly set the principal here. + // however, for the case of mandatory authen, we will want + // to create a CPC using a username as opposed to a null principal. + + Subject subject = clientSubject != null? clientSubject : new Subject(); + String strArray[] = { "Administrator" }; + GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(subject, strArray); + + CallerPrincipalCallback callerPrincipalCallback = null; + if (profile.equals(JASPICData.LAYER_SERVLET)) { + if (messageInfo != null) { + HttpServletRequest req = (HttpServletRequest) messageInfo.getRequestMessage(); + String username = getServletUsername(req); + principalName = getPrincipalNameFromSubject(clientSubject); + strServletContext = req.getServletPath(); + + // better to call cbh with a null principal when the policy is Not + // mandatory + // and with a legitimate principal when the policy is mandatory + // (unless testing send-failure or send-continue - which we are + // not!) + boolean bIsMandatory = isServletAuthMandatory(messageInfo); + if (bIsMandatory) { + isAuthMandatory = true; + debug("GroupPrincipalCallbackSupport() Authentication mandatory"); + if (username != null) { + debug("GroupPrincipalCallbackSupport() auth mandatory, username != null"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, username); + } else if (principalName != null) { + debug("GroupPrincipalCallbackSupport() auth mandatory, principalName != null"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, principalName); + } else { + logMsg("GroupPrincipalCallbackSupport() auth mandatory, username and principalName both == null"); + } + } else { + debug("GroupPrincipalCallbackSupport() Authentication NOT mandatory"); + callerPrincipalCallback = new CallerPrincipalCallback(subject, (Principal) null); + } + } else { + // uses a null principal + debug("GroupPrincipalCallbackSupport(): messageInfo == null, using null principal"); + callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, (Principal) null); + } + } else { + // if here, we were erroneously called by non-servlet profile + debug("WARNING: ServerCallbackSupport.CallerPrincipalCallbackSupport() - profile != servlet."); + callerPrincipalCallback = new CallerPrincipalCallback(subject, (Principal) null); + } + + Callback[] callbacks = new Callback[] { groupPrincipalCallback, callerPrincipalCallback }; + callbackHandler.handle(callbacks); + + // this string will be searched for on client side + String theMessage = "GroupPrincipalCallbackSupport():"; + theMessage += " successfully called callbackHandler.handle(callbacks)"; + theMessage += " for servlet: " + strServletContext; + theMessage += " with isServletAuthMandatory = " + isAuthMandatory; + logMsg(theMessage); + + logMsg("CallbackHandler supports GroupPrincipalCallback"); + + bval = true; // if here assume successful authen + + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support GroupPrincipalCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support GroupPrincipalCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + } + + return bval; + } + + private boolean PasswordValidationCallbackSupport() { + boolean bval = false; + + if (callbackHandler != null) { + try { + Subject subject = new Subject(); + String username = user; // e.g. "j2ee"; + char[] password = passwd.toCharArray(); // e.g. {'j','2','e','e'}; + + PasswordValidationCallback passwordValidationCallback = new PasswordValidationCallback(subject, username, password); + + CallerPrincipalCallback cpc = new CallerPrincipalCallback(subject, (Principal) null); + + Callback[] callbacks = new Callback[] { passwordValidationCallback, cpc }; + + callbackHandler.handle(callbacks); + Subject returnedSubject = passwordValidationCallback.getSubject(); + boolean result = passwordValidationCallback.getResult(); + String userName = passwordValidationCallback.getUsername(); + char[] returnedPassword = passwordValidationCallback.getPassword(); + passwordValidationCallback.clearPassword(); + + // logMsg("PasswordValidation callback returned subject + // ="+returnedSubject); + // logMsg("PasswordValidation callback returned password + // ="+returnedPassword); + logMsg("PasswordValidation callback returned result =" + result); + logMsg("CallbackHandler supports PasswordValidationCallback"); + + bval = result; + + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support PasswordValidationCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support PasswordValidationCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + } + + return bval; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, "In " + profile + " : " + runtimeType + " " + str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSAuthConfigProviderServlet.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSAuthConfigProviderServlet.java new file mode 100644 index 0000000..730c739 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSAuthConfigProviderServlet.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2007, 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import ee.jakarta.tck.authentication.test.basic.sam.config.TSServerAuthConfig; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.ClientAuthConfig; +import jakarta.security.auth.message.config.ServerAuthConfig; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.callback.CallbackHandler; + +/** + * This class is an implementation of the AuthConfigProvider for use by the servlet profile tests. + * + * @author Sun Microsystems + * + */ +public class TSAuthConfigProviderServlet implements AuthConfigProvider { + private static TSLogger logger; + + private HashMap serverAuthConfigMap = new HashMap(); + + private static Map properties = null; + + // This will be called when a vendor registers TSAuthConfigProviderServlet + public TSAuthConfigProviderServlet(Map props, AuthConfigFactory factory) { + properties = props; + + // For self registration + if (factory != null) { + factory.registerConfigProvider(this, null, null, "TSAuthConfigProviderServlet self registration"); + } + + if (logger == null) { + initializeTSLogger(); + } + logger.log(Level.INFO, "invoked TSAuthConfigProviderServlet() constructor(2 args)"); + } + + /** + * This constructor takes a TSLogger instance as a param. + */ + public TSAuthConfigProviderServlet(Map props, TSLogger tsLogger, AuthConfigFactory factory) { + properties = props; + + // For self registration + if (factory != null) { + factory.registerConfigProvider(this, null, null, "TSAuthConfigProviderServlet self registration"); + } + + if (tsLogger != null) { + logger = tsLogger; + } + logger.log(Level.INFO, "invoked TSAuthConfigProviderServlet() constructor(3 args)"); + } + + /** + * Get an instance of ClientAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ClientAuthConfig instance that describes the configuration of + * ClientAuthModules at a given message layer, and for use in an identified application context. + * + * @param layer a String identifying the message layer for the returned ClientAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ClientAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ClientAuthModules encapsulated by ClientAuthContext objects + * derived from the returned ClientAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ClientAuthConfig Object that describes the configuration of ClientAuthModules at the message layer and + * messaging context identified by the layer and appContext arguments. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ClientAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + * + * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + logger.log(Level.INFO, "WARNING: shouldn't get into ClientAuthConfig() for servlet profile"); + + // shouldn't get in here for servlet profile + return null; + } + + /** + * Get an instance of ServerAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ServerAuthConfig instance that describes the configuration of + * ServerAuthModules at a given message layer, and for a particular application context. + * + * @param layer a String identifying the message layer for the returned ServerAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ServerAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ServerAuthModules encapsulated by ServerAuthContext objects + * derived from the returned ServerAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ServerAuthConfig Object that describes the configuration of ServerAuthModules at a given message layer, and + * for a particular application context. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ServerAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + *

+ * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + logger.log(Level.INFO, "TSAuthConfigProviderServlet.getServerAuthConfig() called"); + + String logStr = "TSAuthConfigProviderServlet.getServerAuthConfig" + " : layer=" + layer + " : appContext=" + appContext; + logger.log(Level.INFO, logStr); + try { + if (handler == null) { + // this is used to help verify assertion JASPI:SPEC:71 which + // that we should NOT have a null cbh passed in + String msg = "FAILURE: layer=" + layer + " appContext=" + appContext; + msg += " getServerAuthConfig() received CallbackHandler=null"; + logger.log(Level.INFO, msg); + } else { + String msg = "layer=" + layer + " appContext=" + appContext; + msg += " getServerAuthConfig() received CallbackHandler=non-null"; + logger.log(Level.INFO, msg); + } + + ServerAuthConfig serverAuthConfig = new TSServerAuthConfig(layer, appContext, handler, properties, logger); + serverAuthConfigMap.put(layer + appContext, serverAuthConfig); + + return serverAuthConfig; + } catch (Exception e) { + e.printStackTrace(); + throw new AuthException(e.getMessage()); + } + } + + /** + * Causes a dynamic configuration provider to update its internal state such that any resulting change to its state is + * reflected in the corresponding authentication context configuration objects previously created by the provider within + * the current process context. + * + * @exception AuthException if an error occured during the refresh. + * + * @exception SecurityException if the caller does not have permission to refresh the provider. + */ + @Override + public void refresh() { + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) + return; + else { + try { + logFileLocation = System.getProperty("log.file.location"); + if (logFileLocation != null) { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + boolean appendMode = true; + + // if log file already exists, just append to it + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + JASPICData.DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + } else { + throw new RuntimeException("log.file.location not set"); + } + } catch (Exception e) { + throw new RuntimeException("TSLogger Initialization failed", e); + } + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSFileHandler.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSFileHandler.java new file mode 100644 index 0000000..437cfff --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/TSFileHandler.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.StreamHandler; + +/** + * Simple file Handler based on java.util.logging.FileHandler + * + */ +public class TSFileHandler extends StreamHandler { + private MeteredStream meter; + + private boolean append; + private int limit; + private int count; + private String pattern; + private File files[]; + + // A metered stream is a subclass of OutputStream that + // (a) forwards all its output to a target stream + // (b) keeps track of how many bytes have been written + private class MeteredStream extends OutputStream { + OutputStream out; + + int written; + + MeteredStream(OutputStream out, int written) { + this.out = out; + this.written = written; + } + + @Override + public void write(int b) throws IOException { + out.write(b); + written++; + } + + @Override + public void write(byte buff[]) throws IOException { + out.write(buff); + written += buff.length; + } + + @Override + public void write(byte buff[], int off, int len) throws IOException { + out.write(buff, off, len); + written += len; + } + + @Override + public void flush() throws IOException { + out.flush(); + } + + @Override + public void close() throws IOException { + out.close(); + } + } + + private void open(File fname, boolean append) throws IOException { + int len = 0; + if (append) { + len = (int) fname.length(); + } + FileOutputStream fout = new FileOutputStream(fname.toString(), append); + BufferedOutputStream bout = new BufferedOutputStream(fout); + meter = new MeteredStream(bout, len); + setOutputStream(meter); + } + + // Private method to configure a FileHandler from LogManager + // properties and/or default values as specified in the class + // javadoc. + private void configure() { + LogManager manager = LogManager.getLogManager(); + + String cname = getClass().getName(); + + pattern = ""; + limit = 0; + count = 1; + append = true; + setLevel(Level.ALL); + setFilter(null); + } + + /** + * Initialize a FileHandler to write to the given filename, with optional append. + *

+ * The FileHandler is configured based on LogManager properties (or their default values) except that + * the given pattern argument is used as the filename pattern, the file limit is set to no limit, the file count is set + * to one, and the append mode is set to the given append argument. + *

+ * There is no limit on the amount of data that may be written, so use this with care. + * + * @param pattern the name of the output file + * @param append specifies append mode + * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if the caller does not have + * LoggingPermission("control"). + * @exception IllegalArgumentException if pattern is an empty string + */ + public TSFileHandler(String pattern, boolean append) throws IOException, SecurityException { + if (pattern.length() < 1) { + throw new IllegalArgumentException(); + } + + configure(); + this.pattern = pattern; + this.limit = 0; + this.count = 1; + this.append = append; + openFiles(); + } + + // Private method to open the set of output files, based on the + // configured instance variables. + // + // Note: We don't lock files or rotate files based on size limit. + private void openFiles() throws IOException { + LogManager manager = LogManager.getLogManager(); + manager.checkAccess(); + if (count < 1) { + throw new IllegalArgumentException("file count = " + count); + } + if (limit < 0) { + limit = 0; + } + + int unique = 0; + + files = new File[count]; + for (int i = 0; i < count; i++) { + files[i] = generate(pattern, i, unique); + } + + open(files[0], true); + + } + + // Generate a filename from a pattern. + private File generate(String pattern, int generation, int unique) throws IOException { + File file = null; + String word = ""; + int ix = 0; + boolean sawg = false; + boolean sawu = false; + while (ix < pattern.length()) { + char ch = pattern.charAt(ix); + ix++; + char ch2 = 0; + if (ix < pattern.length()) { + ch2 = Character.toLowerCase(pattern.charAt(ix)); + } + if (ch == '/') { + if (file == null) { + file = new File(word); + } else { + file = new File(file, word); + } + word = ""; + continue; + } else if (ch == '%') { + if (ch2 == 't') { + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir == null) { + tmpDir = System.getProperty("user.home"); + } + file = new File(tmpDir); + ix++; + word = ""; + continue; + } else if (ch2 == 'h') { + file = new File(System.getProperty("user.home")); + if (isSetUID()) { + // Ok, we are in a set UID program. For safety's sake + // we disallow attempts to open files relative to %h. + throw new IOException("can't use %h in set UID program"); + } + ix++; + word = ""; + continue; + } else if (ch2 == 'g') { + word = word + generation; + sawg = true; + ix++; + continue; + } else if (ch2 == 'u') { + word = word + unique; + sawu = true; + ix++; + continue; + } else if (ch2 == '%') { + word = word + "%"; + ix++; + continue; + } + } + word = word + ch; + } + if (count > 1 && !sawg) { + word = word + "." + generation; + } + if (unique > 0 && !sawu) { + word = word + "." + unique; + } + if (word.length() > 0) { + if (file == null) { + file = new File(word); + } else { + file = new File(file, word); + } + } + return file; + } + + /** + * Format and publish a LogRecord. + * + * @param record description of the log event. A null record is silently ignored and is not published + */ + @Override + public synchronized void publish(LogRecord record) { + if (!isLoggable(record)) { + return; + } + super.publish(record); + super.flush(); + flush(); + } + + /** + * Close all the files. + * + * @exception SecurityException if a security manager exists and if the caller does not have + * LoggingPermission("control"). + */ + @Override + public synchronized void close() throws SecurityException { + super.close(); + } + + // Private native method to check if we are in a set UID program. + private static native boolean isSetUID(); + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java new file mode 100644 index 0000000..ce2c7e8 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/SOAPTSServerAuthConfig.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.MessageInfo; +import jakarta.xml.soap.MimeHeaders; +import jakarta.xml.soap.Name; +import jakarta.xml.soap.Node; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPElement; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Iterator; +import java.util.Map; +import javax.security.auth.callback.CallbackHandler; + +public class SOAPTSServerAuthConfig extends TSServerAuthConfig { + + protected SOAPTSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props) { + super(layer, applicationCtxt, cbkHandler, props); + } + + public SOAPTSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props, TSLogger tsLogger) { + super(layer, applicationCtxt, cbkHandler, props); + + if (tsLogger != null) { + logger = tsLogger; + } + + String str = "TSServerAuthConfig called for layer=" + layer + " : appContext=" + applicationCtxt; + logger.log(INFO, str); + } + + @Override + public String getAuthContextID(MessageInfo messageInfo) { + logger.log(INFO, "getAuthContextID called"); + String authContextID = null; + + if (messageLayer.equals(LAYER_SOAP)) { + authContextID = getOpName((SOAPMessage) messageInfo.getRequestMessage()); + + logger.log(INFO, "getAuthContextID() called for layer=" + messageLayer + " shows AuthContextId=" + authContextID); + + } else if (messageLayer.equals(LAYER_SERVLET)) { + super.getAuthContextID(messageInfo); + } + + return authContextID; + } + + private String getOpName(SOAPMessage message) { + if (message == null) { + return null; + } + + String opName = null; + + // First look for a SOAPAction header. + // this is what .net uses to identify the operation + MimeHeaders headers = message.getMimeHeaders(); + if (headers != null) { + String[] actions = headers.getHeader("SOAPAction"); + if (actions != null && actions.length > 0) { + opName = actions[0]; + if (opName != null && opName.equals("\"\"")) { + opName = null; + } + } + } + + // If that doesn't work then we default to trying the name + // of the first child element of the SOAP envelope. + if (opName == null) { + Name name = getName(message); + if (name != null) { + opName = name.getLocalName(); + } + } + + return opName; + } + + private Name getName(SOAPMessage message) { + SOAPPart soap = message.getSOAPPart(); + if (soap == null) { + return null; + } + + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope == null) { + return null; + } + + SOAPBody body = envelope.getBody(); + if (body == null) { + return null; + } + + Iterator childElements = body.getChildElements(); + while (childElements.hasNext()) { + if (childElements.next() instanceof SOAPElement soapElement) { + return soapElement.getElementName(); + } + } + } catch (SOAPException se) { + logger.log(FINE, "WSS: Unable to get SOAP envelope", se); + } + + return null; + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactory.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactory.java new file mode 100644 index 0000000..11c3ede --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactory.java @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.SEVERE; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet; +import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import jakarta.security.auth.message.module.ServerAuthModule; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +/** + * + * @author Raja Perumal + * + * This is an AuthConfigFactory implementation class that will get loaded by the MessageProcessingRuntime (MPR). Once + * this is loaded into the MPR and the the constructor is called, the constructor will call a series of methods that + * will ultimately be performing the assertion tests and logging results. It will be the responsibility of the client + * code to verify results. + * + * Important: It is very likely that the logged messages of this class are being searched for in the logfile by the + * client code. Because of this, refrain from changing log messages in this file. + * + */ +public class TSAuthConfigFactory extends jakarta.security.auth.message.config.AuthConfigFactory { + + private static TSLogger logger = TSLogger.getTSLogger(); + + private static Map authConfigProviderMap = new Hashtable(); + private static Map registrationListenerMap = new Hashtable(); + private static Map registrationContextMap = new Hashtable(); + + private static ProviderConfigurationXMLFileProcessor configFileProcessor; + + public TSAuthConfigFactory() { + // initializeTSLogger(); + logger.log(INFO, "Initialized TSLogger"); + readProviderConfigurationXMLFile(); + } + + /* + * Read the provider configuration XML file and registers each provider with TSAuthConfigFactory + */ + private void readProviderConfigurationXMLFile() { + String providerConfigFileLocation = System.getProperty("provider.configuration.file"); + + if (providerConfigFileLocation == null) { + // looks like prop (via jvm option) is not set correctly + logger.log(SEVERE, "provider.configuration.file property is not properly set/specified"); + System.out.println("provider.configuration.file property is not properly set/specified"); + } + + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + configFileProcessor = new ProviderConfigurationXMLFileProcessor(providerConfigFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + Collection providerConfigurationEntries = + configFileProcessor.getProviderConfigurationEntriesCollection(); + + + // Obtain each ProviderConfigurationEntry and register it with TSAuthConfigFactory + for (ProviderConfigurationEntry providerConfigurationEntry : providerConfigurationEntries) { + if (providerConfigurationEntry != null) { + registerConfigProvider( + providerConfigurationEntry.getProviderClassName(), + providerConfigurationEntry.getProperties(), + providerConfigurationEntry.getMessageLayer(), + providerConfigurationEntry.getApplicationContextId(), + providerConfigurationEntry.getRegistrationDescription()); + } + } + + } catch (Exception e) { + e.printStackTrace(); + if ((e.getMessage() != null) && (!e.getMessage().equals(""))) { + logger.log(SEVERE, e.getMessage()); + } else { + logger.log(SEVERE, "Error in readProviderConfigurationXMLFile()"); + } + } + + } + + /** + * Get a registered AuthConfigProvider from the factory. + * + * Get the provider of ServerAuthConfig and/or ClientAuthConfig objects registered for the identified message layer and + * application context. + * + * @param layer a String identifying the message layer for which the registered AuthConfigProvider is to be returned. + * This argument may be null. + * + * @param appContext a String that identifys the application messaging context for which the registered + * AuthConfigProvider is to be returned. This argument may be null. + * + * @param listener the RegistrationListener whose notify method is to be invoked if the corresponding + * registration is unregistered or replaced. The value of this argument may be null. + * + * @return the implementation of the AuthConfigProvider interface registered at the factory for the layer and appContext + * or null if no AuthConfigProvider is selected. + * + *

+ * All factories shall employ the following precedence rules to select the registered AuthConfigProvider that matches + * the layer and appContext arguments: + *

    + *
  • The provider that is specifically registered for both the corresponding message layer and appContext shall be + * selected. + *
  • if no provider is selected according to the preceding rule, the provider specifically registered for the + * corresponding appContext and for all message layers shall be selected. + *
  • if no provider is selected according to the preceding rules, the provider specifically registered for the + * corresponding message layer and for all appContexts shall be selected. + *
  • if no provider is selected according to the preceding rules, the provider registered for all message layers and + * for all appContexts shall be selected. + *
  • if no provider is selected according to the preceding rules, the factory shall terminate its search for a + * registered provider. + *
+ */ + @Override + public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener) { + AuthConfigProvider localAuthConfigProvider = null; + + // Runtime calls getConfigProvider() after calling + // AuthConfigFactory.getFactory() + // So we can assume TSAuthConfigFactory.getFactory() was called indirectly + logger.log(INFO, "TSAuthConfigFactory.getFactory called Indirectly"); + logger.log(INFO, "TSAuthConfigFactory.getConfigProvider called"); + logger.log(INFO, "getConfigProvider called for Layer : " + layer + " and AppContext :" + appContext); + + if (authConfigProviderMap != null) { + localAuthConfigProvider = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + + // check if a provider is registered for null appContextId (i.e for all + // appContextId) + if (localAuthConfigProvider == null) { + localAuthConfigProvider = (AuthConfigProvider) authConfigProviderMap.get(layer + "null"); + } + // register the listener for AuthConfigProvider + if ((listener != null) && (localAuthConfigProvider != null)) { + registrationListenerMap.put(localAuthConfigProvider, listener); + } + + String logMsg = "TSAuthConfigFactory.getConfigProvider returned non-null provider for"; + logMsg += " Layer : " + layer + " and AppContext :" + appContext; + logger.log(INFO, logMsg); + return localAuthConfigProvider; + } else { + String logMsg = "TSAuthConfigFactory.getConfigProvider returned null provider for"; + logMsg += " Layer : " + layer + " and AppContext :" + appContext; + logger.log(INFO, logMsg); + return null; + } + + } + + /** + * Get the the registration context for the identified registration. + * + * @param registrationID a String that identifies a provider registration at the factory + * + * @return a RegistrationContext or null. When a Non-null value is returned, it is a copy of the registration context + * corresponding to the registration. Null is returned when the registration identifier does not correpond to an active + * registration + */ + @Override + public RegistrationContext getRegistrationContext(String registrationID) { + return (RegistrationContext) registrationContextMap.get(registrationID); + } + + /** + * Get the registration identifiers for all registrations of the provider instance at the factory. + * + * @param provider the AuthConfigurationProvider whose registration identifiers are to be returned. This argument may be + * null, in which case, it indicates that the the id's of all active registration within the factory are returned. + * + * @return an array of String values where each value identifies a provider registration at the factory. This method + * never returns null; it returns an empty array when their are no registrations at the factory for the identified + * provider. + */ + @Override + public String[] getRegistrationIDs(AuthConfigProvider provider) { + Vector keyMatchVector = new Vector(); + + if (provider != null) { + Set entries = authConfigProviderMap.entrySet(); + Iterator iterator = entries.iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry) iterator.next(); + // Add all the matching keys to keyMatchVector + if (entry.getValue().equals(provider)) { + keyMatchVector.add(entry.getKey()); + } + } + } else { + // if provider=null then return all keys from authConfigProviderMap + Set authConfigProviderMapKeySet = authConfigProviderMap.keySet(); + + Iterator iterator = authConfigProviderMapKeySet.iterator(); + while (iterator.hasNext()) { + // Add all keys to keyMatchVector + keyMatchVector.add(iterator.next()); + } + } + + // create the result string array using keyMatchVector + String result[] = new String[keyMatchVector.size()]; + int index = 0; + + Iterator keyMatchVectorIterator = keyMatchVector.iterator(); + + while (keyMatchVectorIterator.hasNext()) { + // populate result string array with the contents of keyMatchVector + result[index++] = (String) keyMatchVectorIterator.next(); + } + + return result; + } + + /** + * Disassociate the listener from all the provider registrations whose layer and appContext values are matched by the + * corresponding arguments to this method. + * + * @param listener the RegistrationListener to be detached. + * + * @param layer a String identifying the message layer or null. + * + * @param appContext a String value identifying the application contex or null. + * + * @return an array of String values where each value identifies a provider registration from which the listener was + * removed. This method never returns null; it returns an empty array if the listener was not removed from any + * registrations. + * + * @exception SecurityException if the caller does not have permission to detach the listener from the factory. + * + */ + + @Override + public String[] detachListener(RegistrationListener listener, String layer, String appContext) { + String[] str = { "" }; + return str; + } + + /** + * Remove the identified provider registration from the factory and invoke any listeners associated with the removed + * registration. + * + * @param registrationID a String that identifies a provider registration at the factory + * + * @return true if there was a registration with the specified identifier and it was removed. Return false if the + * registraionID was invalid. + * + * @exception SecurityException if the caller does not have permission to unregister the provider at the factory. + * + */ + @Override + public boolean removeRegistration(String registrationID) { + // get the corresponding ConfigProvider for registrationID + // and lookup any listeners associated with that provider, if so + // invoke notify method on them + registrationContextMap.remove(registrationID); + return (authConfigProviderMap.remove(registrationID) != null); + } + + /** + * Registers within the factory, a provider of ServerAuthConfig and/or ClientAuthConfig objects for a message layer and + * application context identifier. + * + *

+ * At most one registration may exist within the factory for a given combination of message layer and appContext. Any + * pre-existing registration with identical values for layer and appContext is replaced by a subsequent registration. + * When replacement occurs, the registration identifier, layer, and appContext identifier remain unchanged, and the + * AuthConfigProvider (with initialization properties) and description are replaced. + * + *

+ * Within the lifetime of its Java process, a factory must assign unique registration identifiers to registrations, and + * must never assign a previously used registration identifier to a registration whose message layer and or appContext + * identifier differ from the previous use. + * + *

+ * Programmatic registrations performed via this method must update (according to the replacement rules described + * above), the persistent declarative representation of provider registrations employed by the factory constructor. + * + * @param className the fully qualified name of an AuthConfigProvider implementation class. This argument must not be + * null. + * + * @param properties a Map object containing the initialization properties to be passed to the provider constructor. + * This argument may be null. When this argument is not null, all the values and keys occuring in the Map must be of + * type String. + * + * @param layer a String identifying the message layer for which the provider will be registered at the factory. A null + * value may be passed as an argument for this parameter, in which case, the provider is registered at all layers. + * + * @param appContext a String value that may be used by a runtime to request a configuration object from this provider. + * A null value may be passed as an argument for this parameter, in which case, the provider is registered for all + * configuration ids (at the indicated layers). + * + * @param description a text String descripting the provider. this value may be null. + * + * @return a String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the provider. + * + * @exception SecurityException if the caller does not have permission to register a provider at the factory. + * + * @exception AuthException if the provider construction or registration fails. + */ + @Override + public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description) { + String result = null; + AuthConfigProvider authConfigProvider = null; + + // Hashtable can't store null as key or value so change the + // appcontextId to be string "null" if the input value is null + if (appContext == null) + appContext = "null"; + + if (layer == null) + layer = "null"; + + logger.log(INFO, "registerConfigProvider() called for layer " + layer + " and appContext " + appContext); + try { + // Here we instantiate only TSAuthConfigProvider + // this needs to be revisited. + if (className.equals(TSAuthConfigProvider.class.getName())) { + // instantiate CTS AuthConfigProviderImpl with logger + authConfigProvider = new TSAuthConfigProvider(properties, null, logger); + } else if (className.equals(TSAuthConfigProviderServlet.class.getName())) { + authConfigProvider = new TSAuthConfigProviderServlet(properties, logger, null); + } else { + throw new RuntimeException("Unknown class : " + className); + } + + RegistrationContext previousRegistrationContext = null; + AuthConfigProvider previousAuthConfigProvider = null; + previousAuthConfigProvider = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + previousRegistrationContext = (RegistrationContext) registrationContextMap.get(layer + appContext); + + if (previousAuthConfigProvider == null) { + authConfigProviderMap.put(layer + appContext, authConfigProvider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, true)); + + // Add new provider to the persistent store(ProviderConfiguration.xml) + ProviderConfigurationXMLFileProcessor.addProviderConfigEntry(className, properties, layer, appContext, description); + + } else if ((previousAuthConfigProvider != null) && (previousRegistrationContext.isPersistent() == false)) { + authConfigProviderMap.put(layer + appContext, authConfigProvider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, true)); + + // Add new provider to the persistent store(ProviderConfiguration.xml) + ProviderConfigurationXMLFileProcessor.addProviderConfigEntry(className, properties, layer, appContext, description); + } + result = layer + appContext; + + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Exception :" + e.getMessage()); + } + + return result; + } + + /** + * Registers within the (in-memory) factory, a provider of ServerAuthConfig and/or ClientAuthConfig objects for a + * message layer and application context identifier. This method does NOT effect the factory's persistent declarative + * representation of provider registrations, and intended to be used by Providers to perform self-Registration. + * + *

+ * At most one registration may exist within the factory for a given combination of message layer and appContext. Any + * pre-existing registration with identical values for layer and appContext is replaced by a subsequent registration. + * When replacement occurs, the registration identifier, layer, and appContext identifier remain unchanged, and the + * AuthConfigProvider (with initialization properties) and description are replaced. + * + *

+ * Within the lifetime of its Java process, a factory must assign unique registration identifiers to registrations, and + * must never assign a previously used registration identifier to a registration whose message layer and or appContext + * identifier differ from the previous use. + * + * @param provider the AuthConfigProvider to be registered at the factory (or null). Calling this method with a null + * value for this parameter shall cause getConfigProvider to return null when it iscalled with layer and + * appContext values for which the resulting registration is the best match. + * + * @param layer a String identifying the message layer for which the provider will be registered at the factory. A null + * value may be passed as an argument for this parameter, in which case, the provider is registered at all layers. + * + * @param appContext a String value that may be used by a runtime to request a configuration object from this provider. + * A null value may be passed as an argument for this parameter, in which case, the provider is registered for all + * configuration ids (at the indicated layers). + * + * @param description a text String descripting the provider. this value may be null. + * + * @return a String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the provider. + * + * @exception SecurityException if the caller does not have permission to register a provider at the factory. + * + * @exception AuthException if the provider registration fails. + */ + @Override + public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description) { + + String result = null; + String providerClassName = null; + + logger.log(INFO, "registerConfigProvider() called for layer " + layer + " and appContext " + appContext); + + if (provider == null) { + return result; + } + + try { + RegistrationContext previousRC = null; + AuthConfigProvider previousACP = null; + previousACP = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + previousRC = (RegistrationContext) registrationContextMap.get(layer + appContext); + + if (previousACP == null) { + + authConfigProviderMap.put(layer + appContext, provider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, false)); + + } else if ((previousACP != null) && (previousRC.isPersistent() == true)) { + // update registration context + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, false)); + + if (provider != null) { + providerClassName = provider.getClass().getName(); + } + + // delete existing provider from its persistent state + ProviderConfigurationXMLFileProcessor.deleteProviderConfigEntry(providerClassName, layer, appContext, description); + } + result = layer + appContext; + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Exception :" + e.getMessage()); + } + + return result; + } + + /** + * Registers within the (in-memory) factory, an instance of a ServerAuthModule for a message layer and + * application context identifier as identified by a profile specific context object. + * + *

+ * This will override any other modules that have already been registered, either via proprietary means or using the + * standard API. The ServerAuthModule is removed, via a call to removeServerAuthModule when + * the context associated with the profile specific context object ends. + * + *

+ * Note that this method is a convenience method that can be used instead of registerConfigProvider, but + * should ultimately have the same effect. That is, the layer and appContext parameters are + * generated from the context object, and the ServerAuthModule is wrapped by an implementation specific + * AuthConfigProvider, which are then used to call registerConfigProvider or an internal + * method with the same effect. The returned registration ID is then associated with the profile specific context + * object, and also returned from this method. + * + *

+ * A "profile specific context object" is for example the ServletContext in the Servlet Container Profile. + * The context associated with this ServletContext ends when for example the application corresponding to + * it is undeployed. Association of the registration ID with the ServletContext simply means calling the + * setAttribute method on the ServletContext, with the registration ID as value. (The name + * attribute has not been standardised in this version of the specification) + * + * @param serverAuthModule the ServerAuthModule instance to be registered + * @param context the profile specific context of the application for which the module is registered + * @return A String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the factory. + */ + @Override + public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) { + logger.log(INFO, "registerServerAuthModule() called for serverAuthModule " + serverAuthModule + " and context " + context); + return context.toString(); + } + + /** + * Remove the ServerAuthModule (and potentially encompassing wrappers/factories) that was previously + * registered via a call to registerServerAuthModule. + * + *

+ * Note that this method is a convenience method that can be used instead of removeRegistration, but should + * ultimately have the same effect. That is calling removeRegistration with the return value from + * registerServerAuthModule must have the same effect in that the ServerAuthModule is removed. + * + * @param context the profile specific context of the application for which the module is removed. + */ + @Override + public void removeServerAuthModule(Object context) { + logger.log(INFO, "removeServerAuthModule() called for context " + context); + } + + /** + * Cause the factory to reprocess its persisent declarative representation of provider registrations. + * + *

+ * A factory should only replace an existing registration when a change of provider implementation class or + * initialization properties has occured. + * + * @exception AuthException if an error occured during the reinitialization. + * + * @exception SecurityException if the caller does not have permission to refresh the factory. + */ + @Override + public void refresh() { + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) + return; + else { + try { + logFileLocation = System.getProperty("log.file.location"); + System.out.println("logFileLocation = " + logFileLocation); + if (logFileLocation != null) { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + boolean appendMode = true; + + // create a new file + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + JASPICData.DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + } else { + throw new RuntimeException("log.file.location not set"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("TSLogger Initialization failed", e); + } + } + } + + private String returnString(String[] array) { + String output = ""; + for (int i = 0; i < array.length; i++) { + output += "|" + array[i]; + } + + return output; + } + + private static class RegistrationContextImpl implements RegistrationContext { + private String messageLayer; + + private String appContext; + + private String description; + + private boolean isPersistent; + + private RegistrationContextImpl(String messageLayer, String appContext, String description, boolean isPersistent) { + this.messageLayer = messageLayer; + this.appContext = appContext; + this.description = description; + this.isPersistent = isPersistent; + } + + @Override + public String getMessageLayer() { + return messageLayer; + } + + @Override + public String getAppContext() { + return appContext; + } + + @Override + public String getDescription() { + return description; + } + + /** + * Get the persisted status from the registration context. + * + * @return a boolean indicating whether the registration is the result of a className based registration, or an instance + * based (e.g. self-) registration. Only registrations performed by Class name are persistent. + */ + @Override + public boolean isPersistent() { + return isPersistent; + } + + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactoryForStandalone.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactoryForStandalone.java new file mode 100644 index 0000000..34931c2 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigFactoryForStandalone.java @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.DEFAULT_LOG_FILE; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LOGGER_NAME; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.SEVERE; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet; +import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import jakarta.security.auth.message.module.ServerAuthModule; +import java.util.Collection; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +/** + * + * @author Raja Perumal + * + * This is an AuthConfigFactory implementation class that will get loaded by the MessageProcessingRuntime (MPR). Once + * this is loaded into the MPR and the the constructor is called, the constructor will call a series of methods that + * will ultimately be performing the assertion tests and logging results. It will be the responsibility of the client + * code to verify results. + * + * Important: It is very likely that the logged messages of this class are being searched for in the logfile by the + * client code. Because of this, refrain from changing log messages in this file. + * + */ +public class TSAuthConfigFactoryForStandalone extends jakarta.security.auth.message.config.AuthConfigFactory { + + private static TSLogger logger = null; + private static Map authConfigProviderMap = new Hashtable<>(); + private static Map registrationListenerMap = new Hashtable<>(); + private static Map registrationContextMap = new Hashtable(); + private static ProviderConfigurationXMLFileProcessor configFileProcessor = null; + + public TSAuthConfigFactoryForStandalone() { + initializeTSLogger(); + logger.log(INFO, "Initialized TSLogger"); + readProviderConfigurationXMLFile(); + } + + /* + * Read the provider configuration XML file and registers each provider with TSAuthConfigFactoryForStandalone + */ + private void readProviderConfigurationXMLFile() { + String providerConfigFileLocation = System.getProperty("provider.configuration.file"); + + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration entries as a collection. + configFileProcessor = new ProviderConfigurationXMLFileProcessor(providerConfigFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + Collection providerConfigurationEntries = + configFileProcessor.getProviderConfigurationEntriesCollection(); + + // Obtain each ProviderConfigurationEntry and register it with TSAuthConfigFactoryForStandalone + for (ProviderConfigurationEntry providerConfigurationEntry : providerConfigurationEntries) { + + if (providerConfigurationEntry != null) { + registerConfigProvider( + providerConfigurationEntry.getProviderClassName(), + getCleanACPProps(providerConfigurationEntry.getProperties()), + providerConfigurationEntry.getMessageLayer(), + providerConfigurationEntry.getApplicationContextId(), + providerConfigurationEntry.getRegistrationDescription()); + } + } + + } catch (Exception e) { + e.printStackTrace(); + if ((e.getMessage() != null) && (!e.getMessage().equals(""))) { + logger.log(SEVERE, e.getMessage()); + } else { + logger.log(SEVERE, "Error in readProviderConfigurationXMLFile()"); + } + } + + } + + /* + * When registering a config provider, the properties passed to the ACF.registerConfigProvider() call can ONLY contain + * props t hat are string types. Non-String types must NOT be passed to the registerConfigProvider() method. This method + * will 'clean' a Property map so that its props can be passed to the registerConfigProvider() method. + */ + private Map getCleanACPProps(Map origProps) { + if (origProps == null) { + return null; + } + + HashMap props = new HashMap<>(); + + // loop thru passed in props and remove anything that is + // not of type String since only String type Props are allowed in our + // calls to registerConfigProvider() + for (String key : origProps.keySet()) { + if (key != null) { + Object val = origProps.get(key); + if (val instanceof String) { + // we found entry that is not String so remove it + props.put(key, (String) val); + System.out.println("TSAuthConfigFactoryForStandalone: added key=" + key + " with value = " + val); + } else { + System.out.println("TSAuthConfigFactoryForStandalone: found non-string value for key=" + key); + } + } + } + + return props; + } + + /** + * Get a registered AuthConfigProvider from the factory. + * + * Get the provider of ServerAuthConfig and/or ClientAuthConfig objects registered for the identified message layer and + * application context. + * + * @param layer a String identifying the message layer for which the registered AuthConfigProvider is to be returned. + * This argument may be null. + * + * @param appContext a String that identifys the application messaging context for which the registered + * AuthConfigProvider is to be returned. This argument may be null. + * + * @param listener the RegistrationListener whose notify method is to be invoked if the corresponding + * registration is unregistered or replaced. The value of this argument may be null. + * + * @return the implementation of the AuthConfigProvider interface registered at the factory for the layer and appContext + * or null if no AuthConfigProvider is selected. + * + *

+ * All factories shall employ the following precedence rules to select the registered AuthConfigProvider that matches + * the layer and appContext arguments: + *

    + *
  • The provider that is specifically registered for both the corresponding message layer and appContext shall be + * selected. + *
  • if no provider is selected according to the preceding rule, the provider specifically registered for the + * corresponding appContext and for all message layers shall be selected. + *
  • if no provider is selected according to the preceding rules, the provider specifically registered for the + * corresponding message layer and for all appContexts shall be selected. + *
  • if no provider is selected according to the preceding rules, the provider registered for all message layers and + * for all appContexts shall be selected. + *
  • if no provider is selected according to the preceding rules, the factory shall terminate its search for a + * registered provider. + *
+ */ + @Override + public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener) { + AuthConfigProvider localAuthConfigProvider = null; + + // Runtime calls getConfigProvider() after calling + // AuthConfigFactory.getFactory() + logger.log(INFO, "getConfigProvider called for Layer : " + layer + " and AppContext :" + appContext); + + if (authConfigProviderMap != null) { + localAuthConfigProvider = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + + // register the listener for AuthConfigProvider + if ((listener != null) && (localAuthConfigProvider != null)) { + registrationListenerMap.put(localAuthConfigProvider, listener); + } + + String logMsg = "getConfigProvider returned non-null provider for"; + logMsg += " Layer : " + layer + " and AppContext :" + appContext; + logger.log(INFO, logMsg); + return localAuthConfigProvider; + } else { + String logMsg = "getConfigProvider returned null provider for"; + logMsg += " Layer : " + layer + " and AppContext :" + appContext; + logger.log(INFO, logMsg); + return null; + } + + } + + /** + * Get the the registration context for the identified registration. + * + * @param registrationID a String that identifies a provider registration at the factory + * + * @return a RegistrationContext or null. When a Non-null value is returned, it is a copy of the registration context + * corresponding to the registration. Null is returned when the registration identifier does not correpond to an active + * registration + */ + @Override + public RegistrationContext getRegistrationContext(String registrationID) { + return (RegistrationContext) registrationContextMap.get(registrationID); + } + + /** + * Get the registration identifiers for all registrations of the provider instance at the factory. + * + * @param provider the AuthConfigurationProvider whose registration identifiers are to be returned. This argument may be + * null, in which case, it indicates that the the id's of all active registration within the factory are returned. + * + * @return an array of String values where each value identifies a provider registration at the factory. This method + * never returns null; it returns an empty array when their are no registrations at the factory for the identified + * provider. + */ + @Override + public String[] getRegistrationIDs(AuthConfigProvider provider) { + + Vector keyMatchVector = new Vector(); + + if (provider != null) { + Set entries = authConfigProviderMap.entrySet(); + Iterator iterator = entries.iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry) iterator.next(); + // Add all the matching keys to keyMatchVector + if (entry.getValue().equals(provider)) { + keyMatchVector.add(entry.getKey()); + } + } + } else { + // if provider=null then return all keys from authConfigProviderMap + Set authConfigProviderMapKeySet = authConfigProviderMap.keySet(); + + Iterator iterator = authConfigProviderMapKeySet.iterator(); + while (iterator.hasNext()) { + // Add all keys to keyMatchVector + keyMatchVector.add(iterator.next()); + } + } + + // create the result string array using keyMatchVector + String result[] = new String[keyMatchVector.size()]; + int index = 0; + + Iterator keyMatchVectorIterator = keyMatchVector.iterator(); + + while (keyMatchVectorIterator.hasNext()) { + // populate result string array with the contents of keyMatchVector + result[index++] = (String) keyMatchVectorIterator.next(); + } + + return result; + } + + /** + * Disassociate the listener from all the provider registrations whose layer and appContext values are matched by the + * corresponding arguments to this method. + * + * @param listener the RegistrationListener to be detached. + * + * @param layer a String identifying the message layer or null. + * + * @param appContext a String value identifying the application contex or null. + * + * @return an array of String values where each value identifies a provider registration from which the listener was + * removed. This method never returns null; it returns an empty array if the listener was not removed from any + * registrations. + * + * @exception SecurityException if the caller does not have permission to detach the listener from the factory. + * + */ + @Override + public String[] detachListener(RegistrationListener listener, String layer, String appContext) { + return null; + } + + /** + * Remove the identified provider registration from the factory and invoke any listeners associated with the removed + * registration. + * + * @param registrationID a String that identifies a provider registration at the factory + * + * @return true if there was a registration with the specified identifier and it was removed. Return false if the + * registraionID was invalid. + * + * @exception SecurityException if the caller does not have permission to unregister the provider at the factory. + * + */ + @Override + public boolean removeRegistration(String registrationID) { + // get the corresponding ConfigProvider for registrationID + // and lookup any listeners associated with that provider, if so + // invoke notify method on them + registrationContextMap.remove(registrationID); + return (authConfigProviderMap.remove(registrationID) != null); + } + + /** + * Registers within the factory, a provider of ServerAuthConfig and/or ClientAuthConfig objects for a message layer and + * application context identifier. + * + *

+ * At most one registration may exist within the factory for a given combination of message layer and appContext. Any + * pre-existing registration with identical values for layer and appContext is replaced by a subsequent registration. + * When replacement occurs, the registration identifier, layer, and appContext identifier remain unchanged, and the + * AuthConfigProvider (with initialization properties) and description are replaced. + * + *

+ * Within the lifetime of its Java process, a factory must assign unique registration identifiers to registrations, and + * must never assign a previously used registration identifier to a registration whose message layer and or appContext + * identifier differ from the previous use. + * + *

+ * Programmatic registrations performed via this method must update (according to the replacement rules described + * above), the persistent declarative representation of provider registrations employed by the factory constructor. + * + * @param className the fully qualified name of an AuthConfigProvider implementation class. This argument must not be + * null. + * + * @param properties a Map object containing the initialization properties to be passed to the provider constructor. + * This argument may be null. When this argument is not null, all the values and keys occuring in the Map must be of + * type String. + * + * @param layer a String identifying the message layer for which the provider will be registered at the factory. A null + * value may be passed as an argument for this parameter, in which case, the provider is registered at all layers. + * + * @param appContext a String value that may be used by a runtime to request a configuration object from this provider. + * A null value may be passed as an argument for this parameter, in which case, the provider is registered for all + * configuration ids (at the indicated layers). + * + * @param description a text String descripting the provider. this value may be null. + * + * @return a String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the provider. + * + * @exception SecurityException if the caller does not have permission to register a provider at the factory. + * + * @exception AuthException if the provider construction or registration fails. + */ + @Override + public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description) { + String result = null; + AuthConfigProvider authConfigProvider = null; + String theClass = TSAuthConfigProviderStandalone.class.getName(); + + logger.log(INFO, "registerConfigProvider() called for layer " + layer + " and appContext " + appContext); + try { + // Here we instantiate only TSAuthConfigProvider + // this needs to be revisited. + if (className.equals(TSAuthConfigProvider.class.getName())) { + // instantiate CTS AuthConfigProviderImpl with logger + authConfigProvider = new TSAuthConfigProvider(properties, null, logger); + + } else if (className.equals(TSAuthConfigProviderServlet.class.getName())) { + authConfigProvider = new TSAuthConfigProviderServlet(properties, logger, null); + + } else if (className.equals(theClass)) { + authConfigProvider = new TSAuthConfigProviderStandalone(properties, logger, null); + + } else { + throw new RuntimeException("Unknown class : " + className); + } + + RegistrationContext previousRegistrationContext = null; + AuthConfigProvider previousAuthConfigProvider = null; + previousAuthConfigProvider = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + previousRegistrationContext = (RegistrationContext) registrationContextMap.get(layer + appContext); + + if (previousAuthConfigProvider == null) { + authConfigProviderMap.put(layer + appContext, authConfigProvider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, true)); + + // Add new provider to the persistent store(ProviderConfiguration.xml) + ProviderConfigurationXMLFileProcessor.addProviderConfigEntry(className, properties, layer, appContext, description); + + } else if ((previousAuthConfigProvider != null) && (previousRegistrationContext.isPersistent() == false)) { + authConfigProviderMap.put(layer + appContext, authConfigProvider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, true)); + + // Add new provider to the persistent store(ProviderConfiguration.xml) + ProviderConfigurationXMLFileProcessor.addProviderConfigEntry(className, properties, layer, appContext, description); + } + result = layer + appContext; + + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Exception :" + e.getMessage()); + } + return result; + + } + + /** + * Registers within the (in-memory) factory, a provider of ServerAuthConfig and/or ClientAuthConfig objects for a + * message layer and application context identifier. This method does NOT effect the factory's persistent declarative + * representation of provider registrations, and intended to be used by Providers to perform self-Registration. + * + *

+ * At most one registration may exist within the factory for a given combination of message layer and appContext. Any + * pre-existing registration with identical values for layer and appContext is replaced by a subsequent registration. + * When replacement occurs, the registration identifier, layer, and appContext identifier remain unchanged, and the + * AuthConfigProvider (with initialization properties) and description are replaced. + * + *

+ * Within the lifetime of its Java process, a factory must assign unique registration identifiers to registrations, and + * must never assign a previously used registration identifier to a registration whose message layer and or appContext + * identifier differ from the previous use. + * + * @param provider the AuthConfigProvider to be registered at the factory (or null). Calling this method with a null + * value for this parameter shall cause getConfigProvider to return null when it iscalled with layer and + * appContext values for which the resulting registration is the best match. + * + * @param layer a String identifying the message layer for which the provider will be registered at the factory. A null + * value may be passed as an argument for this parameter, in which case, the provider is registered at all layers. + * + * @param appContext a String value that may be used by a runtime to request a configuration object from this provider. + * A null value may be passed as an argument for this parameter, in which case, the provider is registered for all + * configuration ids (at the indicated layers). + * + * @param description a text String descripting the provider. this value may be null. + * + * @return a String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the provider. + * + * @exception SecurityException if the caller does not have permission to register a provider at the factory. + * + * @exception AuthException if the provider registration fails. + */ + @Override + public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description) { + + String result = null; + String providerClassName = null; + + logger.log(INFO, "registerConfigProvider() called for layer " + layer + " and appContext " + appContext); + + if (provider == null) { + return result; + } + + try { + RegistrationContext previousRC = null; + AuthConfigProvider previousACP = null; + previousACP = (AuthConfigProvider) authConfigProviderMap.get(layer + appContext); + previousRC = (RegistrationContext) registrationContextMap.get(layer + appContext); + + if (previousACP == null) { + authConfigProviderMap.put(layer + appContext, provider); + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, false)); + + } else if ((previousACP != null) && (previousRC.isPersistent() == true)) { + // update registration context + registrationContextMap.put(layer + appContext, new RegistrationContextImpl(layer, appContext, description, false)); + + if (provider != null) { + providerClassName = provider.getClass().getName(); + } + + // delete existing provider from its persistent state + configFileProcessor.deleteProviderConfigEntry(providerClassName, layer, appContext, description); + } + result = layer + appContext; + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Exception :" + e.getMessage()); + } + + return result; + } + + /** + * Registers within the (in-memory) factory, an instance of a ServerAuthModule for a message layer and + * application context identifier as identified by a profile specific context object. + * + *

+ * This will override any other modules that have already been registered, either via proprietary means or using the + * standard API. The ServerAuthModule is removed, via a call to removeServerAuthModule when + * the context associated with the profile specific context object ends. + * + *

+ * Note that this method is a convenience method that can be used instead of registerConfigProvider, but + * should ultimately have the same effect. That is, the layer and appContext parameters are + * generated from the context object, and the ServerAuthModule is wrapped by an implementation specific + * AuthConfigProvider, which are then used to call registerConfigProvider or an internal + * method with the same effect. The returned registration ID is then associated with the profile specific context + * object, and also returned from this method. + * + *

+ * A "profile specific context object" is for example the ServletContext in the Servlet Container Profile. + * The context associated with this ServletContext ends when for example the application corresponding to + * it is undeployed. Association of the registration ID with the ServletContext simply means calling the + * setAttribute method on the ServletContext, with the registration ID as value. (The name + * attribute has not been standardised in this version of the specification) + * + * @param serverAuthModule the ServerAuthModule instance to be registered + * @param context the profile specific context of the application for which the module is registered + * @return A String identifier assigned by the factory to the provider registration, and that may be used to remove the + * registration from the factory. + */ + @Override + public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) { + logger.log(INFO, "registerServerAuthModule() called for serverAuthModule " + serverAuthModule + " and context " + context); + return context.toString(); + } + + /** + * Remove the ServerAuthModule (and potentially encompassing wrappers/factories) that was previously + * registered via a call to registerServerAuthModule. + * + *

+ * Note that this method is a convenience method that can be used instead of removeRegistration, but should + * ultimately have the same effect. That is calling removeRegistration with the return value from + * registerServerAuthModule must have the same effect in that the ServerAuthModule is removed. + * + * @param context the profile specific context of the application for which the module is removed. + */ + @Override + public void removeServerAuthModule(Object context) { + logger.log(INFO, "removeServerAuthModule() called for context " + context); + } + + /** + * Cause the factory to reprocess its persisent declarative representation of provider registrations. + * + *

+ * A factory should only replace an existing registration when a change of provider implementation class or + * initialization properties has occured. + * + * @exception AuthException if an error occured during the reinitialization. + * + * @exception SecurityException if the caller does not have permission to refresh the factory. + */ + @Override + public void refresh() { + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) { + return; + } + + try { + logFileLocation = System.getProperty("log.file.location"); + System.out.println("logFileLocation = " + logFileLocation); + if (logFileLocation == null) { + throw new RuntimeException("log.file.location not set"); + } + + logger = TSLogger.getTSLogger(LOGGER_NAME); + boolean appendMode = true; + + // Create a new file + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("TSLogger Initialization failed", e); + } + + } + + private static class RegistrationContextImpl implements RegistrationContext { + private String messageLayer; + private String appContext; + private String description; + + private RegistrationContextImpl(String messageLayer, String appContext, String description, boolean isPersistent) { + this.messageLayer = messageLayer; + this.appContext = appContext; + this.description = description; + } + + @Override + public String getMessageLayer() { + return messageLayer; + } + + @Override + public String getAppContext() { + return appContext; + } + + @Override + public String getDescription() { + return description; + } + + /** + * Get the persisted status from the registration context. + * + * @return a boolean indicating whether the registration is the result of a className based registration, or an instance + * based (e.g. self-) registration. Only registrations performed by Class name are persistent. + */ + @Override + public boolean isPersistent() { + return false; + } + + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java new file mode 100644 index 0000000..c36386e --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProvider.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.basic.sam.AuthDataCallbackHandler; +import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.ClientAuthConfig; +import jakarta.security.auth.message.config.ServerAuthConfig; +import java.util.HashMap; +import java.util.Map; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSAuthConfigProvider implements jakarta.security.auth.message.config.AuthConfigProvider { + + private static TSLogger logger; + private static Map properties; + private HashMap clientAuthConfigMap = new HashMap(); + private HashMap serverAuthConfigMap = new HashMap(); + + // This will be called when a vendor registers TSAuthConfigProvider + public TSAuthConfigProvider(Map props, AuthConfigFactory factory) { + properties = props; + + // For self registration + if (factory != null) { + factory.registerConfigProvider(this, null, null, "TSAuthConfig Provider self registration"); + } + + if (logger == null) { + initializeTSLogger(); + } + } + + // TSAuthConfigFactory invokes this constructor with TSLogger + public TSAuthConfigProvider(Map props, AuthConfigFactory factory, TSLogger tsLogger) { + properties = props; + + // For self registration + if (factory != null) { + factory.registerConfigProvider(this, null, null, "TSAuthConfig Provider self registration"); + } + + if (tsLogger != null) + logger = tsLogger; + + } + + /** + * Get an instance of ClientAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ClientAuthConfig instance that describes the configuration of + * ClientAuthModules at a given message layer, and for use in an identified application context. + * + * @param layer a String identifying the message layer for the returned ClientAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ClientAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ClientAuthModules encapsulated by ClientAuthContext objects + * derived from the returned ClientAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ClientAuthConfig Object that describes the configuration of ClientAuthModules at the message layer and + * messaging context identified by the layer and appContext arguments. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ClientAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + * + * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + String logStr = "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=" + layer + " : " + "appContext=" + appContext; + + logger.log(INFO, logStr); + + try { + if (handler == null) { + // instantiate a default callback handler that gets + // username and password from the environment using + // system property j2eelogin.name j2eelogin.password + handler = new AuthDataCallbackHandler(); + } else { + // even if we receive vendor callbackhandler replace it with our own + // callbackhandler for jaspic test. + System.out.println("Received callbackHandler =" + handler.getClass().getName()); + handler = new AuthDataCallbackHandler(); + } + + ClientAuthConfig clientAuthConfig = new TSClientAuthConfig(layer, appContext, handler, properties, logger); + clientAuthConfigMap.put(layer + appContext, clientAuthConfig); + return clientAuthConfig; + } catch (Exception e) { + throw new AuthException(e.getMessage()); + } + } + + /** + * Get an instance of ServerAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ServerAuthConfig instance that describes the configuration of + * ServerAuthModules at a given message layer, and for a particular application context. + * + * @param layer a String identifying the message layer for the returned ServerAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ServerAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ServerAuthModules encapsulated by ServerAuthContext objects + * derived from the returned ServerAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ServerAuthConfig Object that describes the configuration of ServerAuthModules at a given message layer, and + * for a particular application context. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ServerAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + *

+ * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + String logStr = "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=" + layer + " : " + "appContext=" + appContext; + + logger.log(INFO, logStr); + try { + + if ((!layer.equals(JASPICData.LAYER_SERVLET)) && (handler == null)) { + // instantiate a default callback handler that gets + // username and password from the environment using + // system property j2eelogin.name j2eelogin.password + handler = new AuthDataCallbackHandler(); + } else if ((layer.equals(JASPICData.LAYER_SERVLET)) && (handler == null)) { + // this is used to help verify assertion JASPI:SPEC:71 which + // that we should NOT have a null cbh passed in + String msg = "FAILURE: layer=" + layer + " appContext=" + appContext; + msg += " getServerAuthConfig() received CallbackHandler=null"; + logger.log(INFO, msg); + } + + ServerAuthConfig serverAuthConfig = null; + + if (JASPICData.LAYER_SOAP.equals(layer)) { + serverAuthConfig = new SOAPTSServerAuthConfig(layer, appContext, handler, properties, logger); + } else { + serverAuthConfig = new TSServerAuthConfig(layer, appContext, handler, properties, logger); + } + + serverAuthConfigMap.put(layer + appContext, serverAuthConfig); + return serverAuthConfig; + } catch (Exception e) { + throw new AuthException(e.getMessage()); + } + } + + /** + * Causes a dynamic configuration provider to update its internal state such that any resulting change to its state is + * reflected in the corresponding authentication context configuration objects previously created by the provider within + * the current process context. + * + * @exception AuthException if an error occured during the refresh. + * + * @exception SecurityException if the caller does not have permission to refresh the provider. + */ + @Override + public void refresh() { + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) + return; + else { + try { + logFileLocation = System.getProperty("log.file.location"); + if (logFileLocation != null) { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + boolean appendMode = true; + + // if log file already exists, just append to it + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + JASPICData.DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + } else { + throw new RuntimeException("log.file.location not set"); + } + } catch (Exception e) { + throw new RuntimeException("TSLogger Initialization failed", e); + } + } + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProviderStandalone.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProviderStandalone.java new file mode 100644 index 0000000..5c34273 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSAuthConfigProviderStandalone.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2007, 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.ClientAuthConfig; +import jakarta.security.auth.message.config.ServerAuthConfig; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.callback.CallbackHandler; + +/** + * This class is an implementation of the AuthConfigProvider for use by the core tests. This is used to support the + * scenario when a profile other than Servlet/SOAP is being defined and used with Jakarta Authentication. + * + * At the time of this test creation, the 2 main supported profiles are SOAP and Servlet. But it is very possible that + * someone may use Jakarta Authentication to define their own profiles. For that case, we need to have something we can use for the + * core tests besides SOAP/Servlet tests. This is used to support that case. + * + * @author Sun Microsystems + */ +public class TSAuthConfigProviderStandalone implements jakarta.security.auth.message.config.AuthConfigProvider { + + // this is a default string used for Persistent Registration description + public static String DESC_KEY = "description_key"; + + private static TSLogger logger; + + private HashMap serverAuthConfigMap = new HashMap(); + + private static Map properties = null; + + private String description = null; + + // This will be called when a vendor registers TSAuthConfigProviderStandalone + public TSAuthConfigProviderStandalone(Map props, AuthConfigFactory factory) { + this(props, null, factory, null); + + logger.log(Level.INFO, "invoked TSAuthConfigProviderStandalone() constructor(2 args)"); + } + + /** + * This constructor takes a TSLogger instance as a param. + */ + public TSAuthConfigProviderStandalone(Map props, TSLogger tsLogger, AuthConfigFactory factory) { + this(props, tsLogger, factory, null); + + logger.log(Level.INFO, "invoked TSAuthConfigProviderStandalone() constructor(3 args)"); + } + + /** + * This constructor takes a TSLogger instance as a param. + */ + public TSAuthConfigProviderStandalone(Map props, TSLogger tsLogger, AuthConfigFactory factory, String description) { + properties = props; + + // For self registration + if (factory != null) { + factory.registerConfigProvider(this, null, null, "TSAuthConfigProviderStandalone self registration"); + } + + if (tsLogger != null) { + this.logger = tsLogger; + } else { + initializeTSLogger(); + } + + if (description != null) { + // this is usually the case for in-memory registration + this.description = description; + } else { + // this is usually the case for persistent registration + this.description = getDescFromProps(props); + if (this.description == null) { + this.description = "PERSIST_DEFAULT"; + } + } + + logger.log(Level.INFO, "invoked TSAuthConfigProviderStandalone() constructor(4 args)"); + } + + private String getDescFromProps(Map props) { + String strDesc = null; + if (props != null) { + strDesc = (String) props.get(DESC_KEY); + } + + return strDesc; + } + + /** + * Get an instance of ClientAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ClientAuthConfig instance that describes the configuration of + * ClientAuthModules at a given message layer, and for use in an identified application context. + * + * @param layer a String identifying the message layer for the returned ClientAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ClientAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ClientAuthModules encapsulated by ClientAuthContext objects + * derived from the returned ClientAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ClientAuthConfig Object that describes the configuration of ClientAuthModules at the message layer and + * messaging context identified by the layer and appContext arguments. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ClientAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + * + * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + logger.log(Level.INFO, "WARNING: shouldnt get into ClientAuthConfig() for servlet profile"); + + // shouldnt get in here for servlet profile + return null; + } + + /** + * Get an instance of ServerAuthConfig from this provider. + * + *

+ * The implementation of this method returns a ServerAuthConfig instance that describes the configuration of + * ServerAuthModules at a given message layer, and for a particular application context. + * + * @param layer a String identifying the message layer for the returned ServerAuthConfig object. This argument must not + * be null. + * + * @param appContext a String that identifies the messaging context for the returned ServerAuthConfig object. This + * argument must not be null. + * + * @param handler a CallbackHandler to be passed to the ServerAuthModules encapsulated by ServerAuthContext objects + * derived from the returned ServerAuthConfig. This argument may be null, in which case the implementation may assign a + * default handler to the configuration. + * + * @return a ServerAuthConfig Object that describes the configuration of ServerAuthModules at a given message layer, and + * for a particular application context. This method does not return null. + * + * @exception AuthException if this provider does not support the assignment of a default CallbackHandler to the + * returned ServerAuthConfig. + * + * @exception SecurityException if the caller does not have permission to retrieve the configuration. + *

+ * The CallbackHandler assigned to the configuration must support the Callback objects required to be supported by the + * profile of this specification being followed by the messaging runtime. The CallbackHandler instance must be + * initialized with any application context needed to process the required callbacks on behalf of the corresponding + * application. + */ + @Override + public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException { + logger.log(Level.INFO, "TSAuthConfigProviderStandalone.getServerAuthConfig() called"); + + String logStr = "TSAuthConfigProviderStandalone.getServerAuthConfig" + " : layer=" + layer + " : appContext=" + appContext; + logger.log(Level.INFO, logStr); + try { + if (handler == null) { + // this is used to help verify assertion JASPI:SPEC:71 which + // that we should NOT have a null cbh passed in + String msg = "FAILURE: layer=" + layer + " appContext=" + appContext; + msg += " getServerAuthConfig() received CallbackHandler=null"; + logger.log(Level.INFO, msg); + } else { + String msg = "layer=" + layer + " appContext=" + appContext; + msg += " getServerAuthConfig() received CallbackHandler=non-null"; + logger.log(Level.INFO, msg); + } + + ServerAuthConfig serverAuthConfig = new TSServerAuthConfig(layer, appContext, handler, properties, logger); + serverAuthConfigMap.put(layer + appContext, serverAuthConfig); + + return serverAuthConfig; + } catch (Exception e) { + e.printStackTrace(); + throw new AuthException(e.getMessage()); + } + } + + /** + * Causes a dynamic configuration provider to update its internal state such that any resulting change to its state is + * reflected in the corresponding authentication context configuration objects previously created by the provider within + * the current process context. + * + * @exception AuthException if an error occured during the refresh. + * + * @exception SecurityException if the caller does not have permission to refresh the provider. + */ + @Override + public void refresh() { + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) + return; + else { + try { + logFileLocation = System.getProperty("log.file.location"); + if (logFileLocation != null) { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + boolean appendMode = true; + + // if log file already exists, just append to it + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + JASPICData.DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + } else { + throw new RuntimeException("log.file.location not set"); + } + } catch (Exception e) { + throw new RuntimeException("TSLogger Initialization failed", e); + } + } + } + + public String getDescription() { + return description; + } + + public void setDescription(String val) { + description = val; + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthConfig.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthConfig.java new file mode 100644 index 0000000..46d4c6b --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthConfig.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.config.ClientAuthContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.xml.soap.MimeHeaders; +import jakarta.xml.soap.Name; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPElement; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSClientAuthConfig implements jakarta.security.auth.message.config.ClientAuthConfig { + + private static String messageLayer; + private static String appContext; + private static CallbackHandler callbackHandler; + private static TSLogger logger; + + private Map properties = null; + + /** + * Creates a new instance of ClientAuthConfigImpl + */ + public TSClientAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props) { + messageLayer = layer; + appContext = applicationCtxt; + callbackHandler = cbkHandler; + properties = props; + } + + public TSClientAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props, TSLogger tsLogger) { + this(layer, applicationCtxt, cbkHandler, props); + logger = tsLogger; + String str = "TSClientAuthConfig called for layer=" + layer + " : appContext=" + applicationCtxt; + logger.log(Level.INFO, str); + } + + /** + * Get the authentication context identifier corresponding to the request and response objects encapsulated in + * messageInfo. + * + * @param messageInfo a contextual Object that encapsulates the client request and server response objects. + * + * @return the operation identifier related to the encapsulated request and response objects, or null. + * + * @throws IllegalArgumentException if the type of the message objects incorporated in messageInfo are not compatible + * with the message types supported by this authentication context configuration object. + */ + @Override + public String getAuthContextID(MessageInfo messageInfo) { + String rval = null; + logger.log(Level.INFO, "TSClientAuthConfig.getOperation called"); + if (messageLayer.equals(JASPICData.LAYER_SOAP)) { + + return getOpName((SOAPMessage) messageInfo.getRequestMessage()); + + } else if (messageLayer.equals(JASPICData.LAYER_SERVLET)) { + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + rval = request.getServletPath() + " " + request.getMethod(); + return rval; + } else + return null; + } + + private String getOpName(SOAPMessage message) { + if (message == null) { + return null; + } + + String rvalue = null; + + // first look for a SOAPAction header. + // this is what .net uses to identify the operation + MimeHeaders headers = message.getMimeHeaders(); + if (headers != null) { + String[] actions = headers.getHeader("SOAPAction"); + if (actions != null && actions.length > 0) { + rvalue = actions[0]; + if (rvalue != null && rvalue.equals("\"\"")) { + rvalue = null; + } + } + } + + // if that doesn't work then we default to trying the name + // of the first child element of the SOAP envelope. + if (rvalue == null) { + Name name = getName(message); + if (name != null) { + rvalue = name.getLocalName(); + } + } + + return rvalue; + } + + private Name getName(SOAPMessage message) { + Name rvalue = null; + SOAPPart soap = message.getSOAPPart(); + if (soap != null) { + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope != null) { + SOAPBody body = envelope.getBody(); + if (body != null) { + Iterator it = body.getChildElements(); + while (it.hasNext()) { + Object o = it.next(); + if (o instanceof SOAPElement) { + rvalue = ((SOAPElement) o).getElementName(); + break; + } + } + } + } + } catch (SOAPException se) { + logger.log(Level.INFO, "WSS: Unable to get SOAP envelope"); + } + } + + return rvalue; + } + + /** + * Causes a dynamic anthentication context configuration object to update the internal state that it uses to process + * calls to its getAuthContext method. + * + * @exception AuthException if an error occured during the update. + * + * @exception SecurityException if the caller does not have permission to refresh the configuration object. + */ + @Override + public void refresh() { + + } + + /** + * Get the message layer name of this authentication context configuration object. + * + * @return the message layer name of this configuration object, or null if the configuration object pertains to an + * unspecified message layer. + */ + @Override + public String getMessageLayer() { + return messageLayer; + } + + /** + * Get the application context identifier of this authentication context configuration object. + * + * @return the String identifying the application context of this configuration object or null if the configuration + * object pertains to an unspecified application context. + */ + @Override + public String getAppContext() { + return appContext; + } + + /** + * Get a ClientAuthContext instance from this ClientAuthConfig. + * + *

+ * The implementation of this method returns a ClientAuthContext instance that encapsulates the ClientAuthModules used + * to secure and validate requests/responses associated with the given operation. + * + *

+ * Specifically, this method accesses this ClientAuthConfig object with the argument operation to determine the + * ClientAuthModules that are to be encapsulated in the returned ClientAuthContext instance. + * + *

+ * The ClientAuthConfig object establishes the request and response MessagePolicy objects that are passed to the + * encapsulated modules when they are initialized by the returned ClientAuthContext instance. It is the modules' + * responsibility to enforce these policies when invoked. + * + * @param operation an operation identifier used to index the provided config, or null. This value must be + * identical to the value returned by the getOperation method for all MessageInfo objects + * passed to the secureRequest method of the returned ClientAuthContext. + * + * @param clientSubject a Subject that represents the source of the service request to be secured by the acquired + * authentication context. The principal and/or credentials of the Subject may be used to select or acquire the + * authentication context. If the Subject is not null, additional Principals or credentials (pertaining to the source of + * the request) may be added to the Subject. A null value may be passed to for this parameter. + * + * @param properties a Map object that may be used by the caller to augment the properties that will be passed to the + * encapsulated modules at module initialization. The null value may be passed for this parameter. + * + * @return a ClientAuthContext instance that encapsulates the ClientAuthModules used to secure and validate + * requests/responses associated with the given operation, or null (indicating that no modules are configured). + * + * @exception AuthException if this operation fails. + */ + @Override + public ClientAuthContext getAuthContext(String operation, Subject clientSubject, Map properties) throws AuthException { + + // Copy properties that are passed in this method to this.properties + // + // Note: this.properties is obtained from the Provider which gets those + // properties during provider registration time from the factory. + this.properties.putAll(properties); + + try { + + String logStr = "TSClientAuthConfig.getAuthContext: layer=" + messageLayer + " : appContext=" + appContext; + logger.log(Level.INFO, logStr); + + logger.log(Level.INFO, "TSClientAuthConfig.getAuthContext: layer=" + messageLayer + " : appContext=" + appContext + + " operationId=" + operation); + + ClientAuthContext clientAuthContext = new TSClientAuthContext(messageLayer, appContext, callbackHandler, operation, + clientSubject, this.properties, logger); + + logStr = "TSClientAuthConfig.getAuthContext: returned non-null" + " ClientAuthContext for operationId=" + operation; + logger.log(Level.INFO, logStr); + return clientAuthContext; + } catch (Exception e) { + throw new AuthException(e.getMessage()); + } + + } + + /** + * Used to determine whether the authentication context configuration object encapsulates any protected authentication + * contexts. + * + * @return true if the configuration object encapsulates at least one protected authentication context. Otherwise, this + * method returns false. + */ + @Override + public boolean isProtected() { + // To verify protected code path, always return true. + return true; + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java new file mode 100644 index 0000000..db66f38 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSClientAuthContext.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSAuthExceptionClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSFailureClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendFailureClientAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendSuccessClientAuthModule; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.module.ClientAuthModule; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSClientAuthContext implements jakarta.security.auth.message.config.ClientAuthContext { + + private static ClientAuthModule clientAuthModule; + private static TSLogger logger; + + public TSClientAuthContext() { + + } + + public TSClientAuthContext(String messageLayer, String appContext, CallbackHandler handler, String operation, Subject clientSubject, + Map properties, TSLogger tsLogger) throws AuthException { + + this(messageLayer, appContext, handler, operation, clientSubject, properties); + + logger = tsLogger; + ClientAuthModule cam = null; + logger.log(INFO, "TSClientAuthContext called"); + + // Pass TSlogger to TSServerAuthModule through properties + properties.put("TSLogger", logger); + + if (messageLayer.equals(LAYER_SOAP)) { + if (appContext.indexOf("SendSuccessHello") > -1) { + cam = new TSSendSuccessClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("SendFailureHello") > -1) { + cam = new TSSendFailureClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("FailureHello") > -1) { + cam = new TSFailureClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("AuthExceptionHello") > -1) { + cam = new TSAuthExceptionClientAuthModule(); + cam.initialize(null, null, handler, properties); + + } else { + cam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSClientAuthModule(); + cam.initialize(null, null, handler, properties); + } + } else if (messageLayer.equals(LAYER_SERVLET)) { + cam = new TSClientAuthModule(); + cam.initialize(null, null, handler, properties); + } + + clientAuthModule = cam; + } + + private TSClientAuthContext(String layer, String appContxt, CallbackHandler hndler, String operatn, Subject cliSubject, Map props) throws AuthException { + + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + logger.log(INFO, "TSClientAuthContext.secureRequest called"); + + return clientAuthModule.secureRequest(messageInfo, clientSubject); + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + logger.log(INFO, "TSClientAuthContext.validateResponse called"); + + return clientAuthModule.validateResponse(messageInfo, clientSubject, serviceSubject); + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + clientAuthModule.cleanSubject(messageInfo, subject); + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSRegistrationListener.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSRegistrationListener.java new file mode 100644 index 0000000..229aeda --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSRegistrationListener.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.RegistrationListener; + +public class TSRegistrationListener implements RegistrationListener { + + String profileLayer; + String appContext; + boolean notified = false; + AuthConfigFactory acf = null; + + public TSRegistrationListener() { + this.profileLayer = null; + this.appContext = null; + acf = AuthConfigFactory.getFactory(); + } + + public TSRegistrationListener(String profileLayer, String appContext) { + this.profileLayer = profileLayer; + this.appContext = appContext; + acf = AuthConfigFactory.getFactory(); + } + + public String getProfileLayer() { + return this.profileLayer; + } + + public void setProfileLayer(String val) { + this.profileLayer = val; + } + + public String getAppContext() { + return this.appContext; + } + + public void setAppContext(String val) { + this.appContext = val; + } + + public void resetNotifyFlag() { + notified = false; + } + + public boolean notified() { + return notified; + } + + /* + * check if notifications should occur by verifying if our actual notification (ie wasNotified) matches with our + * expected notification (ie notified()) return true on success and false otherwise. + */ + public boolean check(String layer, String context) { + boolean match = false; + boolean rval = true; + + if ((layer == null || ((profileLayer != null) && profileLayer.equals(layer))) + && (context == null || ((appContext != null) && appContext.equals(context)))) { + match = true; + } + + String msg = "TSRegistrationListener: layer=" + profileLayer + ", appcontext="; + msg += appContext + " ExpectedVal=" + notified() + " actualVal=" + match + "for layer=" + layer + " context=" + context; + + if (match && notified()) { + // should have been notification - which matches whats in notified() + rval = true; + } else if (match && !notified()) { + rval = false; + debug("in check(): " + msg); + } + + return rval; + } + + /* + * Notify the listener that a registration with which it was associated was replaced or unregistered. + */ + @Override + public void notify(String layer, String context) { + notified = true; + boolean bLayersMatch = (profileLayer == layer) || profileLayer.equals(layer); + boolean bContextsMatch = (appContext == context) || appContext.equals(context); + + if (bLayersMatch && bContextsMatch) { + // successful notification + debug("successful notification for layer=" + layer + " context=" + context); + } else { + // error - notify had problem + String msg = "ERROR - listener notified at wrong profileLayer: " + layer + " or context: " + context; + debug(msg); + } + } + + private void debug(String out) { + System.out.println(out); + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthConfig.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthConfig.java new file mode 100644 index 0000000..13c8da6 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthConfig.java @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.SEVERE; + +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.config.ServerAuthContext; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + * + * Important: It is very likely that the logged messages of this class are being searched for in the logfile by the + * client code. Because of this, refrain from changing log messages in this file. + * + */ +public class TSServerAuthConfig implements jakarta.security.auth.message.config.ServerAuthConfig { + + protected static TSLogger logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + + protected static String messageLayer; + protected static String appContext; + protected static CallbackHandler handler; + protected static Map properties; + protected static Map authMandatoryMap; + + protected TSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props) { + messageLayer = layer; + appContext = applicationCtxt; + handler = cbkHandler; + properties = props; + } + + public TSServerAuthConfig(String layer, String applicationCtxt, CallbackHandler cbkHandler, Map props, TSLogger tsLogger) { + this(layer, applicationCtxt, cbkHandler, props); + + if (tsLogger != null) { + logger = tsLogger; + } + + String str = "TSServerAuthConfig called for layer=" + layer + " : appContext=" + applicationCtxt; + logger.log(INFO, str); + } + + /** + * Get the message layer name of this authentication context configuration object. + * + * @return the message layer name of this configuration object, or null if the configuration object pertains to an + * unspecified message layer. + */ + @Override + public String getMessageLayer() { + logger.log(INFO, "getMessageLayer called"); + return messageLayer; + } + + /** + * Get the application context identifier of this authentication context configuration object. + * + * @return the String identifying the application context of this configuration object or null if the configuration + * object pertains to an unspecified application context. + */ + @Override + public String getAppContext() { + logger.log(INFO, "getAppContext called"); + return appContext; + } + + /** + * Get the authentication context identifier corresponding to the request and response objects encapsulated in + * messageInfo. + * + * @param messageInfo a contextual Object that encapsulates the client request and server response objects. + * + * @return the auth context identifier related to the encapsulated request and response objects, or null. + * + * @throws IllegalArgumentException if the type of the message objects incorporated in messageInfo are not compatible + * with the message types supported by this authentication context configuration object. + * + */ + + @Override + public String getAuthContextID(MessageInfo messageInfo) { + logger.log(INFO, "getAuthContextID called"); + String authContextID = null; + + if (messageLayer.equals(JASPICData.LAYER_SERVLET)) { + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + authContextID = request.getServletPath() + " " + request.getMethod(); + + dumpServletProfileKeys(messageInfo, "getAuthContextID", JASPICData.LAYER_SERVLET); + } else { + authContextID = null; + } + + String logMsg = "getAuthContextID() called for layer=" + messageLayer; + logMsg += " shows AuthContextId=" + authContextID; + logger.log(INFO, logMsg); + + return authContextID; + } + + /* + * This is a convenience method that will likely only be called once. Currently there is only one key to dump from the + * map, but his may change in the future. + */ + private void dumpServletProfileKeys(MessageInfo msgInfo, String callerMethod, String messageLayer) { + Map map = msgInfo.getMap(); + + // for debugging only + // dumpAllKeyValues(map); + + // lets pull out some servlet info that we can use to help uniquely + // identify the source of this request + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + String servletName = request.getServletPath(); + + // see assertion JASPI:SPEC:306 (section 3.8.1.1) for details on this + // jsr-196 states the following key must exist for servlet profile + String strKey = "jakarta.security.auth.message.MessagePolicy.isMandatory"; + if (map != null) { + String keyVal = (String) map.get(strKey); + String msg = "dumpServletProfileKeys() called with attrs: "; + msg += " layer=" + messageLayer; + msg += " servletName=" + servletName; + msg += " callerMethod=" + callerMethod; + msg += " key=" + strKey; + + if (keyVal == null) { + msg += " value=Invalid"; + addMandatoryStatusToMap(msgInfo, messageLayer, appContext, false); + } else { + // We dont care if Boolean.valueOf(keyVal).booleanValue() is true + // or false. We only care that it is a boolean value -which means + // this is considered a valid key-value pair. + msg += " value=Valid"; + addMandatoryStatusToMap(msgInfo, messageLayer, appContext, true); + } + + logger.log(INFO, msg); + } + } + + private void addMandatoryStatusToMap(MessageInfo msgInfo, String layer, String appContext, boolean isMandatory) { + if (authMandatoryMap == null) { + authMandatoryMap = new Hashtable<>(); + } + + // see if entry already exists and if not we need to add the entry + Boolean booleanObj = authMandatoryMap.get(layer + appContext); + if (booleanObj == null) { + authMandatoryMap.put(layer + appContext, Boolean.valueOf(isMandatory)); + } + } + + private boolean getMandatoryStatusFromMap(String layer, String appContext) { + boolean mandatoryStatus = false; + + if (layer.equals("JASPICData.LAYER_SERVLET")) { + if (authMandatoryMap != null) { + Boolean booleanObj = authMandatoryMap.get(layer + appContext); + mandatoryStatus = booleanObj.booleanValue(); + } else { + String msg = "Could Not properly determine isMandatory status."; + msg += " Will return false."; + logger.log(INFO, msg); + mandatoryStatus = false; + } + } + + return mandatoryStatus; + } + + /* + * for debug purposes only + */ + private void dumpAllKeyValues(Map map) { + if (map == null) { + logger.log(INFO, "map is null"); + return; + } + + Set keys = map.keySet(); + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + String msg = ""; + String key = (String) iterator.next(); + if (key != null) { + msg = "key=" + key; + String val = (String) map.get(key); + if (val != null) { + msg += " value=" + val; + } else { + msg += " value=null"; + } + logger.log(INFO, msg); + } + } + } + + /** + * Causes a dynamic anthentication context configuration object to update the internal state that it uses to process + * calls to its getAuthContext method. + * + * @exception AuthException if an error occured during the update. + * + * @exception SecurityException if the caller does not have permission to refresh the configuration object. + */ + @Override + public void refresh() { + logger.log(INFO, "refresh called"); + } + + /** + * Used to determine whether the authentication context configuration object encapsulates any protected authentication + * contexts. + * + * @return true if the configuration object encapsulates at least one protected authentication context. Otherwise, this + * method returns false. + */ + @Override + public boolean isProtected() { + // To verify protected code path, always return true. + return true; + } + + /** + * Get a ServerAuthContext instance from this ServerAuthConfig. This method should get called by the MPR. + * + *

+ * The implementation of this method returns a ServerAuthContext instance that encapsulates the ServerAuthModules used + * to validate requests and secure responses associated with the given operation. + * + *

+ * Specifically, this method accesses this ServerAuthConfig object with the argument operation to determine the + * ServerAuthModules that are to be encapsulated in the returned ServerAuthContext instance. + * + *

+ * The ServerAuthConfig object establishes the request and response MessagePolicy objects that are passed to the + * encapsulated modules when they are initialized by the returned ServerAuthContext instance. It is the modules' + * responsibility to enforce these policies when invoked. + * + * @param operation an operation identifier used to index the provided config, or null. This value must be + * identical to the value returned by the getOperation method for all MessageInfo objects + * passed to the validateRequest method of the returned ServerAuthContext. + * + * @param serviceSubject a Subject that represents the source of the service response to be secured by the acquired + * authentication context. The principal and/or credentials of the Subject may be used to select or acquire the + * authentication context. If the Subject is not null, additional Principals or credentials (pertaining to the source of + * the response) may be added to the Subject. A null value may be passed to for this parameter. + * + * @param properties a Map object that may be used by the caller to augment the properties that will be passed to the + * encapsulated modules at module initialization. The null value may be passed for this parameter. + * + * @return a ServerAuthContext instance that encapsulates the ServerAuthModules used to secure and validate + * requests/responses associated with the given operation, or null (indicating that no modules are configured). + * + * @exception AuthException if this operation fails. + */ + @Override + public ServerAuthContext getAuthContext(String operation, Subject serviceSubject, Map properties) throws AuthException { + String logStr = "TSServerAuthConfig.getAuthContext: layer=" + messageLayer + " : appContext=" + appContext; + logger.log(INFO, logStr); + + logger.log(INFO, + "TSServerAuthConfig.getAuthContext: layer=" + messageLayer + " : appContext=" + appContext + " operationId=" + operation); + + if (serviceSubject != null) { + properties.put(JASPICData.SVC_SUBJECT_KEY, serviceSubject); + logger.log(INFO, "found a non-null serviceSubject in getAuthContext()"); + } + + // Copy properties that are passed in this method to this.properties + // + // Note: this.properties is obtained from the Provider which gets those + // properties during provider registration time from the factory. + try { + if (TSServerAuthConfig.properties != null && properties != null) { + TSServerAuthConfig.properties.putAll(properties); + } + } catch (Exception ex) { + logger.log(INFO, "Exception : " + ex.getMessage()); + } + + checkIf115Compatible(properties); + + // validate operation is correct for the message layer type + validateOperationId(operation, messageLayer); + + try { + boolean bIsMand = getMandatoryStatusFromMap(messageLayer, appContext); + ServerAuthContext serverAuthContext = + new TSServerAuthContext( + messageLayer, appContext, handler, operation, serviceSubject, this.properties, + bIsMand, logger); + + logStr = "TSServerAuthConfig.getAuthContext: returned non-null ServerAuthContext"; + logger.log(INFO, logStr); + + // For SOAP layer + logStr = "TSServerAuthConfig.getAuthContext: returned non-null" + " ServerAuthContext for operationId=" + operation; + logger.log(INFO, logStr); + return serverAuthContext; + } catch (Exception e) { + logger.log(SEVERE, "Got AuthException in TSServerAuthConfig.getAuthContext"); + logger.log(SEVERE, e.getMessage()); + throw new AuthException(e.getMessage()); + } + } + + /* + * this is used to help us verify JASPI:SPEC:300 This is a convenience method that will likely only be called once. + * Currently there is only one key to dump from the props, but this may change in the future. + */ + private void checkIf115Compatible(Map properties) { + String msg = "layer=" + messageLayer + " appContext=" + appContext; + + // Jakarta Authentication states the following key must exist if Jakarta Authorization is supported + // but it must NOT exist if 115 is NOT supported. + String strKey = "jakarta.security.jacc.PolicyContext"; + msg += " Key=" + strKey; + if (properties != null) { + String keyVal = (String) properties.get(strKey); + + if (keyVal == null) { + msg += " does NOT exist thus Not 115 compatible"; + } else if (keyVal != null) { + msg += " does exist thus 115 compatible"; + logger.log(INFO, "key=" + strKey + " value=" + keyVal); // XXXX - + // debugging + } + } else { + msg += " does NOT exist thus Not 115 compatible"; + } + + logger.log(INFO, msg); + } + + /* + * The goal of this method is to help us verify if we have a properly constructed operationID. This method will log + * messages indicating if we do or do not have a valid operationID. + */ + private void validateOperationId(String operation, String layer) { + String MNAME = "TSServerAuthConfig.validateOperationId() : "; + String sProfile; + String lstr; + boolean bPassed = true; + + operation = operation.trim(); + if (operation == null) { + logger.log(SEVERE, MNAME + " there was a null operationId and should not be!"); + } + + if (layer.equals(JASPICData.LAYER_SERVLET)) { + // name should consist of following format + // + // there should ONLY be one whitspace + sProfile = "HttpServlet profile"; + if (operation.startsWith("http:")) { + // should not start with a context path + lstr = MNAME + sProfile + " should not start with a context path"; + logger.log(SEVERE, lstr); + bPassed = false; + } + + if ((operation.indexOf(" ") != operation.lastIndexOf(" "))) { + // if here then we have more than one whitespace and should not! + lstr = MNAME + sProfile + " found more than one whitespace."; + logger.log(SEVERE, lstr); + bPassed = false; + } + + int ii = operation.indexOf(" "); + String httpMethod = operation.substring(ii); + httpMethod = httpMethod.trim(); + if (!(httpMethod.equalsIgnoreCase("GET") || httpMethod.equalsIgnoreCase("POST") || httpMethod.equalsIgnoreCase("PUT"))) { + // it should be one of these three supported http methods + lstr = MNAME + sProfile + " invalid http method type :" + httpMethod + ". Must be POST|GET|PUT"; + logger.log(SEVERE, lstr); + bPassed = false; + } + + if (bPassed) { + logger.log(INFO, MNAME + sProfile + " : PASSED"); + } else { + logger.log(SEVERE, MNAME + sProfile + " : FAILED"); + } + + } else if (layer.equals(JASPICData.LAYER_SOAP)) { + // Nothing to verify here for SOAP profile + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java new file mode 100644 index 0000000..6f4902e --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/config/TSServerAuthContext.java @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.config; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SOAP; +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.SVC_SUBJECT_KEY; +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.servlet.TSServletWrapperSAM; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSAuthExceptionServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSFailureServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendFailureServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSSendSuccessServerAuthModule; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.module.ServerAuthModule; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + * + * Important: It is very likely that the logged messages of this class are being searched for in the logfile by the + * client code. Because of this, refrain from changing log messages in this file. + * + */ +public class TSServerAuthContext implements jakarta.security.auth.message.config.ServerAuthContext { + + private static ServerAuthModule serverAuthModule; + private static String messageLayer; + private static Map properties; + private static TSLogger logger; + + private static String soapUPTokenAppContext = "HelloService HelloPort"; + private static String servletUPTokenAppContext = "spitests_servlet_web"; + + private static MessageInfo messageInfoFromVerifyReq; + + private static boolean isMandatory = false; + + /** Creates a new instance of ServerAuthContext */ + public TSServerAuthContext() { + } + + public TSServerAuthContext(String messageLayer, String appContext, CallbackHandler handler, String operation, Subject clientSubject, + Map properties, boolean isAuthMandatory, TSLogger tsLogger) throws AuthException { + this(messageLayer, appContext, handler, operation, clientSubject, properties); + + logger = tsLogger; + logger.log(INFO, "TSServerAuthContext called"); + String logStr = "TSServerAuthContext called for messageLayer=" + messageLayer + " : appContext=" + appContext; + logger.log(INFO, logStr); + logStr += " : operation=" + operation; + logger.log(INFO, logStr); + + ServerAuthModule sam = null; + properties.put("TSLogger", logger); + + isMandatory = isAuthMandatory; + + // Create a TargetPolicy for AUTHENTICATE_SENDER + MessagePolicy.TargetPolicy msgTargetPolicy = + + new MessagePolicy.TargetPolicy(null, new MessagePolicy.ProtectionPolicy() { + @Override + public String getID() { + return AUTHENTICATE_SENDER; + } + }); + MessagePolicy.TargetPolicy[] msgTargetPolicies = { msgTargetPolicy }; + MessagePolicy requestMessagePolicy = new MessagePolicy(msgTargetPolicies, isMandatory); + + // Create a TargetPolicy for AUTHENTICATE_CONTENT + MessagePolicy.TargetPolicy msgTargetPolicyContent = + + new MessagePolicy.TargetPolicy(null, new MessagePolicy.ProtectionPolicy() { + @Override + public String getID() { + return AUTHENTICATE_CONTENT; + } + }); + MessagePolicy.TargetPolicy[] msgTargetPoliciesContent = { msgTargetPolicyContent }; + MessagePolicy responseMessagePolicy = new MessagePolicy(msgTargetPoliciesContent, isMandatory); + + // Note: We could also choose auth modules based on appContext but + // MessageLayer seems to be the ideal candidate for choosing + // auth modules. + if (messageLayer.equals(LAYER_SOAP)) { + + if (appContext.equals(soapUPTokenAppContext)) { + sam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSServerAuthModule(); + sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); + } else if (appContext.indexOf("SendSuccessHello") > -1) { + sam = new TSSendSuccessServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("SendFailureHello") > -1) { + sam = new TSSendFailureServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("FailureHello") > -1) { + sam = new TSFailureServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else if (appContext.indexOf("AuthExceptionHello") > -1) { + sam = new TSAuthExceptionServerAuthModule(); + sam.initialize(null, null, handler, properties); + + } else { + sam = new ee.jakarta.tck.authentication.test.basic.sam.module.soap.TSServerAuthModule(); + sam.initialize(null, null, handler, properties); + } + + } else if (messageLayer.equals(LAYER_SERVLET)) { + System.out.println("AppContext =" + appContext); + + if (appContext.contains(servletUPTokenAppContext) && operation.contains("WrapperServlet")) { + sam = new TSServletWrapperSAM(); + sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); + } else if (appContext.contains(servletUPTokenAppContext)) { + sam = new TSServerAuthModule(); + sam.initialize(requestMessagePolicy, responseMessagePolicy, handler, properties); + } else { + sam = new TSServerAuthModule(); + sam.initialize(requestMessagePolicy, null, handler, properties); + } + } + + serverAuthModule = sam; + } + + /* + * This should be private so that we can be sure people pass a valid TSLogger into the public constructor. + */ + private TSServerAuthContext(String layer, String appCtxt, CallbackHandler callbackHandler, String optn, Subject clientSubject, Map props) throws AuthException { + messageLayer = layer; + properties = props; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When paththis status value is returned to challenge an application request message, the challenged request must be + * saved by the authentication module such that it can be recovered when the module's validateRequest message is called + * to process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + Object reqObj = null; + + String msg = "TSServerAuthContext.validateRequest called"; + + // This msg used to verify assertion: JASPI:SPEC:50 + logger.log(INFO, msg); + + // This msg used to verify assertion: JASPI:SPEC:88 + msg += " for layer=" + messageLayer; + logger.log(INFO, msg); + + // The following msg used to verify assertion: JASPI:SPEC:89 + if (messageInfo != null) { + reqObj = messageInfo.getRequestMessage(); + if (reqObj != null) { + // if here, we want to see if our reqObj is type HttpServletRequest + if (reqObj instanceof HttpServletRequest) { + String contextPath = ((HttpServletRequest) reqObj).getContextPath(); + String servletPath = ((HttpServletRequest) reqObj).getServletPath(); + + // note: we are leaving off the pathInfo since we only want + // to log contextPath and servletPath info so it's + // more of a pseudo requestURI we are validating but + // note that this will match our client so should be fine. + String requestURI = contextPath + servletPath; + msg += " for requestURI=" + requestURI; + } else { + msg += " reqObj instanceof=" + reqObj.getClass().getName(); + } + } else { + msg += " reqObj=NULL"; + } + } + + // Used to assist with the verification of assertion JASPI:SPEC:52 + String subjStr = messageLayer + " profile: "; + if (clientSubject != null) { + subjStr += "TSServerAuthContext.validateRequest called with non-null client Subject"; + } else { + subjStr += "TSServerAuthContext.validateRequest called with null client Subject"; + } + logger.log(INFO, subjStr); + + // used to assist with the verification of assertion JASPI:SPEC:52 + verifyClientSubject(clientSubject); + + // used to assist with the verification of assertion JASPI:SPEC:53 and + // JASPI:SPEC:313 + verifyServiceSubject(serviceSubject); + + AuthStatus authStatus = serverAuthModule.validateRequest(messageInfo, clientSubject, serviceSubject); + + logger.log(INFO, msg); + + dumpAuthStatusString(authStatus, msg); + + // save off the MessageInfo object instance so that we can verify the + // same one is used in the call to SecureRequest (JASPI:SPEC:60) + messageInfoFromVerifyReq = messageInfo; + + return authStatus; + } + + public void dumpAuthStatusString(AuthStatus rval, String msg) { + if (msg == null) { + msg = ""; + } + + if (rval == AuthStatus.SUCCESS) { + msg += " AuthStatus=AuthStatus.SUCCESS"; + } else if (rval == AuthStatus.FAILURE) { + msg += " AuthStatus=AuthStatus.FAILURE"; + } else if (rval == AuthStatus.SEND_SUCCESS) { + msg += " AuthStatus=AuthStatus.SEND_SUCCESS"; + } else if (rval == AuthStatus.SEND_FAILURE) { + msg += " AuthStatus=AuthStatus.SEND_FAILURE"; + } else if (rval == AuthStatus.SEND_CONTINUE) { + msg += " AuthStatus=AuthStatus.SEND_CONTINUE"; + } else { + msg += " AuthStatus=" + rval; + } + + logger.log(INFO, msg); + } + + /* + * This verifies the client subject object for validateRequest() calls. This is used to assist with the verification of + * assertion JASPI:SPEC:52 by logging information about the state of the clientSubject The spec (section 2.1.5) states + * that clientSubject can not be null and that it can not be read-only. + * + */ + public void verifyClientSubject(Subject clientSubject) { + String msg = ""; + + if (clientSubject == null) { + // this is failure against the spec + msg = "FAILURE detected - ClientSubjects should not be null."; + } else { + msg = "Valid ClientSubjects - it was not null."; + } + logger.log(INFO, msg); + + if (clientSubject != null) { + if (clientSubject.isReadOnly()) { + // this is failure against the spec + msg = "FAILURE detected - ClientSubjects should not be read-only."; + } else { + msg = "Valid ClientSubjects - it was not read-only."; + } + logger.log(INFO, msg); + } + + } + + /* + * This verifies the service subject object for validateRequest() calls. This is used to assist with the verification of + * assertion JASPI:SPEC:53 by logging information regarding wether or not the serviceSubject passed into validateRequest + * was the same as what was used to acquire the ServerAuthContext. + */ + public void verifyServiceSubject(Subject serviceSubject) { + String msg = ""; + String key = JASPICData.SVC_SUBJECT_KEY; + Subject value = (Subject) properties.get(key); + + if (value != null) { + msg = "got a non-null subject out of the map"; + logger.log(INFO, msg); + + if (serviceSubject == null) { + msg = "FAILURE detected - ServiceSubjects should be the same and are not."; + } else if (value.equals(serviceSubject)) { + msg = "ServiceSubjects correctly matched."; + } else { + // if here, serviceSubjects dont match but should + msg = "FAILURE detected - ServiceSubjects should be the same and are not."; + } + logger.log(INFO, msg); + + if ((serviceSubject != null) && (value.equals(serviceSubject))) { + // to help verify assertion JASPI:SPEC:313 (per spec section 2.1.5.2): + // If a non-null Subject was used to call the ServerAuthContext + // the same Subject must be passed as the serviceSubject in this + // call. If a non-null serviceSubject is used in this call, it + // must not be read-only + if (serviceSubject.isReadOnly()) { + // should not get here. + msg = "FAILURE detected - ServiceSubjects should not be read-only."; + logger.log(INFO, msg); + } else { + msg = "Valid ServiceSubjects - it was not read-only."; + logger.log(INFO, msg); + } + } + } + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + AuthStatus rval = AuthStatus.SUCCESS; + Object reqObj = null; + Object respObj = null; + String msg = "TSServerAuthContext.secureResponse called"; + + // This msg used to verify assertion: JASPI:SPEC:130 + logger.log(INFO, msg); + + msg = "secureResponse called for layer=" + messageLayer; + + try { + if (messageInfo != null) { + + verifyMessageInfoObjsMatch(messageInfo); // JASPI:SPEC:60 + + reqObj = messageInfo.getRequestMessage(); + respObj = messageInfo.getResponseMessage(); + if (reqObj != null) { + // if here, we want to see if our reqObj is type HttpServletRequest + if (reqObj instanceof HttpServletRequest) { + String contextPath = ((HttpServletRequest) reqObj).getContextPath(); + String servletPath = ((HttpServletRequest) reqObj).getServletPath(); + + // note: we are leaving off the pathInfo since we only want + // to log contextPath and servletPath info so it's + // more of a pseudo requestURI we are validating but + // note that this will match our client so should be fine. + String requestURI = contextPath + servletPath; + msg += " for requestURI=" + requestURI; + } else { + msg += " reqObj instanceof=" + reqObj.getClass().getName(); + } + } else { + msg += " reqObj=NULL"; + } + } + logger.log(INFO, msg); + + // used to assist with the verification of assertion JASPI:SPEC:61 + verifySecureRespServiceSubject(serviceSubject); + + rval = serverAuthModule.secureResponse(messageInfo, serviceSubject); + msg = ""; + dumpAuthStatusString(rval, msg); + + if (rval.equals(AuthStatus.SUCCESS)) { + // explicitly set return code but remember that we must be sure the + // response we stuff into the MessageInfo is wrapped in a + // HttpServletResponseWrapper object (jsr-196 spec section 3.8.3.4) + if (respObj != null) { + ((HttpServletResponse) respObj).setStatus(HttpServletResponse.SC_OK); + HttpServletResponseWrapper respWrapper = null; + if (!(reqObj instanceof jakarta.servlet.http.HttpServletResponseWrapper)) { + respWrapper = new HttpServletResponseWrapper((HttpServletResponse) respObj); + respWrapper.setStatus(HttpServletResponse.SC_OK); + messageInfo.setResponseMessage(respWrapper); + } else { + messageInfo.setResponseMessage(respObj); + } + + logger.log(INFO, "Set the responseObjects return status to OK==200"); + } else { + logger.log(INFO, "ResponseObject == null so we could NOT set response status"); + } + } else { + logger.log(INFO, "authStatus != SUCCESS so not setting response object status == 200"); + } + } catch (AuthException ex) { + logger.log(INFO, "Got AuthException"); + ex.printStackTrace(); + throw ex; + } catch (Exception ex) { + logger.log(INFO, "Got generic Exception"); + ex.printStackTrace(); + } + + return rval; + } + + /* + * This is used to assist with verifying assertion: JASPI:SPEC:60 This is a convenience method that is used to verify if + * the passed in messageInfo object (which should be from secureRequest()) matches our previously saved off messageInfo + * object (which should be from our last call to validateRequest() - which we saved off in a static var. + */ + public void verifyMessageInfoObjsMatch(MessageInfo messageInfo) { + String err = "FAILURE: MessageInfo object in secureRequest does not"; + err += " match the messageInfo object from validateRequest"; + + String msg = "MessageInfo object from secureRequest matches the "; + msg += " messageInfo object from validateRequest"; + + // verify the passed in messageInfo + if ((messageInfo == null) && (messageInfoFromVerifyReq == null)) { + logger.log(INFO, msg); + } else if ((messageInfo == null) || (messageInfoFromVerifyReq == null)) { + logger.log(INFO, err); + if (messageInfo == null) { + msg = "Failure: secureRequest had null obj but validateRequest did not"; + logger.log(INFO, msg); + } else { + msg = "Failure: validateRequest had null obj but secureResponse did not"; + logger.log(INFO, msg); + } + } else if (!messageInfoFromVerifyReq.equals(messageInfo)) { + logger.log(INFO, err); + logger.log(INFO, "FAILURE: messageInfo objects non-null but don't match"); + } else { + logger.log(INFO, msg); + } + + return; + } + + /* + * This verifies the service subject object for secureResponse() calls. Spec says: (re: Pt 2 in MPR) "If a non-null + * serviceSubject is used in this call, it must not be read-only and the same serviceSubject must be passed in the call + * to secureResponse for the corresponding response (if there is one)." + * + */ + public void verifySecureRespServiceSubject(Subject serviceSubject) { + Subject subjectFromProperties = (Subject) properties.get(SVC_SUBJECT_KEY); + + if (subjectFromProperties != null) { + logger.log(INFO, "Got a non-null subject out of the map"); + + if (serviceSubject == null) { + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."); + } else if (subjectFromProperties.equals(serviceSubject)) { + logger.log(INFO, "SecureResponse ServiceSubjects correctly matched."); + } else { + // If here, service subjects don't match but should + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."); + } + + if ((serviceSubject != null) && (subjectFromProperties.equals(serviceSubject))) { + // To help verify assertion JASPI:SPEC:313 (per spec section 2.1.5.2): + // If a non-null Subject was used to call the ServerAuthContext + // the same Subject must be passed as the serviceSubject in this + // call. If a non-null serviceSubject is used in this call, it + // must not be read-only + if (serviceSubject.isReadOnly()) { + // Should not get here. + logger.log(INFO, "FAILURE detected - SecureResponse ServiceSubjects should not be read-only."); + } else { + logger.log(INFO, "Valid SecureResponse ServiceSubjects - it was not read-only."); + } + } + } + + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logger.log(INFO, "TSServerAuthContext.cleanSubject called"); + + serverAuthModule.cleanSubject(messageInfo, subject); + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSClientAuthModule.java new file mode 100644 index 0000000..b1704ef --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSClientAuthModule.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.servlet; + +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * This is a placeholder file that should not end up getting used by the servlet profile. Ideally we should be able to + * search the log file for strings from this class and we should not see any of those entries getting logged. + * + * @author Sun Microsystems + */ +public class TSClientAuthModule implements jakarta.security.auth.message.module.ClientAuthModule { + private TSLogger logger = null; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSClientAuthModule() { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + logMsg("TSClientAuthModule() constructor called."); + } + + public TSClientAuthModule(TSLogger log) { + if (log != null) { + logger = log; + } else { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + } + logMsg("TSClientAuthModule(TSLogger) constructor called."); + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) + throws AuthException { + + if ((options != null) && (options.get("TSLogger") != null)) { + logger = (TSLogger) options.get("TSLogger"); + } + + logMsg("TSClientAuthModule.initialize() called."); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSClientAuthModule.getSupportedMessageTypes() called."); + Class[] classarray = { jakarta.servlet.http.HttpServletRequest.class, jakarta.servlet.http.HttpServletResponse.class }; + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + + logMsg("TSClientAuthModule.secureRequest() called."); + return AuthStatus.SUCCESS; + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + logMsg("TSClientAuthModule.validateResponse() called."); + return AuthStatus.SUCCESS; + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSClientAuthModule.cleanSubject() called."); + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSRequestWrapper.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSRequestWrapper.java new file mode 100644 index 0000000..4c90978 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSRequestWrapper.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.servlet; + +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import java.util.logging.Level; + +public class TSRequestWrapper extends HttpServletRequestWrapper { + private TSLogger logger = null; + + Map optionsMap = null; + + public TSRequestWrapper(HttpServletRequest request) { + super(request); + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + logMsg("TSRequestWrapper constructor called"); + } + + @Override + public Object getAttribute(String name) { + + if ("isRequestWrapped".equals(name)) { + return Boolean.TRUE; + } + + return super.getAttribute(name); + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + + boolean bval = super.authenticate(response); + + debug("made it into TSRequestWrapper.authenticate()"); + + // + // NOTE: + // It is not clear that flow will make it into this method. So we will + // write out possible errors messages below and then check for occurances + // of those error messages from within the tests in spi/servlet. + // + + // do some checks and validation relates to JASPIC 1.1 spec + // section 3.8.4 (para 1) per assertion JASPIC:SPEC:322 + if (bval) { + String msg = ""; + // "Both cases, must also ensure that the value returned by calling + // getAuthType on the HttpServletRequest is consistent in terms of + // being null or non-null with the value returned by getUserPrincipal." + if ((super.getAuthType() != null) && super.getRemoteUser() != null) { + // This is good - both non-null so this is okay + msg = "HttpServletRequest authentication results match with getAuthType() and getRemoteUser()"; + } else if ((super.getAuthType() == null) && super.getRemoteUser() == null) { + // This is good - both null, so this is okay too + msg = "HttpServletRequest authentication results match with getAuthType() and getRemoteUser()"; + } else { + // This is bad - must be mismatch between getAuthType() and + // getRemoteUser() + msg = "ERROR - HttpServletRequest authentication result mis-match with getAuthType() and getRemoteUser()"; + } + logger.log(Level.INFO, msg); + } + + // test for assertion: JASPIC:SPEC:323 from spec section 3.8.4, para 2: + // check if getAuthType() != null, and if not null, then check if + // MessageInfo Map + // sets/users key=jakarta.servlet.http.authType. If so, getAuthType should be + // set + // set to value of key. getAuthType should not be null on successful authN. + if (bval) { + String msg = ""; + + if ((super.getAuthType() != null) && (optionsMap != null)) { + // see if key=jakarta.servlet.http.authType exists and if so, make + // sure it matches the getAuthType() value + if (optionsMap.get("jakarta.servlet.http.authType") != null) { + // if here, then we need to make sure the value specified for + // getAuthType matches this value. + String val = (String) optionsMap.get("jakarta.servlet.http.authType"); + if (val == null) { + // spec violation - cant be null if key exists!!! + msg = "ERROR - invalid setting for jakarta.servlet.http.authType = null"; + } else if (!val.equalsIgnoreCase(super.getAuthType())) { + // spec violation - these have to match!! + msg = "ERROR - mismatch value set for jakarta.servlet.http.authType and getAuthType()"; + } else { + // we are good if here. + msg = "getAuthType() matches value for jakarta.servlet.http.authType"; + } + logger.log(Level.INFO, msg); + debug(msg); + debug("authenticate(): getAuthType() = " + super.getAuthType()); + debug("authenticate(): jakarta.servlet.http.authType = " + val); + } + } + + } + + return bval; + } + + public void setOptionsMap(Map options) { + optionsMap = options; + } + + public Map getOptionsMap() { + return optionsMap; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + public void debug(String str) { + System.out.println("TSRequestWrapper: " + str); + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSResponseWrapper.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSResponseWrapper.java new file mode 100644 index 0000000..d5a1683 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSResponseWrapper.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.servlet; + +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import java.util.logging.Level; + +public class TSResponseWrapper extends HttpServletResponseWrapper { + private TSLogger logger = null; + + public TSResponseWrapper(HttpServletResponse response) { + super(response); + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + logMsg("TSResponseWrapper constructor called"); + } + + @Override + public String getHeader(String name) { + + if ("isResponseWrapped".equals(name)) { + return "true"; + } + + return super.getHeader(name); + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServerAuthModule.java new file mode 100644 index 0000000..760f2c5 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServerAuthModule.java @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.servlet; + +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.basic.sam.CommonCallbackSupport; +import ee.jakarta.tck.authentication.test.basic.sam.ServerCallbackSupport; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Sun Microsystems + */ +public class TSServerAuthModule implements jakarta.security.auth.message.module.ServerAuthModule { + private TSLogger logger; + + private static CallbackHandler callbackHandler; + + /** + * Creates a new instance of TSServerAuthModule + */ + public TSServerAuthModule() { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + logMsg("TSServerAuthModule() constructor called"); + } + + public TSServerAuthModule(TSLogger log) { + if (log != null) { + logger = log; + } else { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + } + logMsg("TSServerAuthModule(TSLogger) constructor called"); + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) + throws AuthException { + + if ((options != null) && options.get("TSLogger") != null) { + logger = (TSLogger) options.get("TSLogger"); + } + + callbackHandler = handler; + + // perform some checking to support assertion JASPI:SPEC:87 + verifyRequestPolicy(requestPolicy); + + logger.log(INFO, "CBH for HttpServlet supports type: " + handler.getClass().getName()); + } + + /* + * This is a convenience method that will do some verification on the request policy to see if it complies with + * assertion JASPI:SPEC:87. If there are any problems found, appropriate log statements will be made and searched for + * later on in the Client code. + */ + private void verifyRequestPolicy(MessagePolicy requestPolicy) { + + String errStr = "Layer=" + JASPICData.LAYER_SERVLET; + errStr += " requestPolicy=invalid in TSServerAuthModule.initialize()"; + + if (requestPolicy == null) { + // we should never have a null requestpolicy here + logger.log(Level.SEVERE, errStr); + } else { + MessagePolicy.TargetPolicy[] tp = requestPolicy.getTargetPolicies(); + if (tp.length < 1) { + // must return an array containing at least one TargetPolicy + logger.log(INFO, errStr); + } else { + for (int ii = 0; ii < tp.length; ii++) { + MessagePolicy.ProtectionPolicy pp = tp[ii].getProtectionPolicy(); + if ((pp != null) && (!isProtectionPolicyIDValid(pp.getID()))) { + String str = "Layer=" + JASPICData.LAYER_SERVLET; + str += " Invalid ProtectionPolicy.getID()"; + logger.log(INFO, str); + } + } + } + } + } + + /* + * (spec section 3.7.4) For servlet profile, calling the getID() method on the ProtectionPolicy must return one of the + * following values: ProtectionPolicy.AUTHENTICATE_SENDER ProtectionPolicy.AUTHENTICATE_CONTENT + */ + public boolean isProtectionPolicyIDValid(String strId) { + boolean bval = false; + + if ((strId.equals(MessagePolicy.ProtectionPolicy.AUTHENTICATE_CONTENT)) + || (strId.equals(MessagePolicy.ProtectionPolicy.AUTHENTICATE_SENDER))) { + bval = true; + } + return bval; + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.servlet.http.HttpServletRequest.class, jakarta.servlet.http.HttpServletResponse.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + String servletPath = request.getContextPath() + request.getServletPath(); + + String msg = "HttpServlet profile: "; + + if (clientSubject != null) { + msg += "TSServerAuthModule.validateRequest called with non-null client Subject"; + // msg += " principal=" + getPrincipalNameFromSubject(clientSubject); + } else { + msg += "TSServerAuthModule.validateRequest called with null client Subject"; + } + logMsg(msg); + + // lets ensure we are not pre-logged in + doCheckForPreLogin(messageInfo, clientSubject, serviceSubject); + + if (serviceSubject != null) { + msg = msg + " with serviceSubject :" + getPrincipalNameFromSubject(serviceSubject); + } else { + msg = msg + " with null serviceSubject"; + } + logMsg(msg); + + // verify any profile keys + logMessageTypes(messageInfo, "validateRequest"); + dumpServletProfileKeys(messageInfo, "validateRequest"); + + // this is used to help us verify part of JASPI:SPEC:98 + // if auth is optional then we should be able to have a null + // principal. If auth is mandatory, then we need to set a + // principal object with some valid creds. + boolean bIsMandatory = isAuthMandatory(messageInfo); + + // support to test validateRequest is called regardless of whether authN + // is required (including when isMandatory is false) + logMsg("validateRequest() called for " + servletPath + ", isMandatory() = " + bIsMandatory); + + // Check Callback Handler support for server runtime + logMsg("Dispatching to request for servletPath: " + servletPath); + CommonCallbackSupport commonCallbacks = new CommonCallbackSupport(logger, callbackHandler, JASPICData.LAYER_SERVLET, + "ServerRuntime"); + commonCallbacks.verify(); + + doServerCallbackChecks(messageInfo, clientSubject, serviceSubject); + + // Determine return status based on pre-canned context ids + AuthStatus returnStatus = getReturnStatus(messageInfo, clientSubject); + + setSpecialRequestAttribute(messageInfo, "validateReqCalled", "true"); + + return returnStatus; + } + + /* + * This method checks if a user is pre-logged in for a servlet that required no authen. In such a case, we should NOT + * see any pre-authenticated status. This method should be called before any callback handler invocations occur. + */ + private boolean doCheckForPreLogin(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) { + + boolean rval = true; + String theServlet = "OpenToAllServlet"; + + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + String servletPath = request.getContextPath() + request.getServletPath(); + + if (servletPath.contains(theServlet)) { + // we found our request for the servlet that should have + // open perms and thus NOT require any authentication. + Principal callerPrincipal = request.getUserPrincipal(); + + if (callerPrincipal == null) { + // if here, we are good since we are not logged in (based on the + // premise that if logged in, calling getUserPrincipal() would + // have returned a non-null value.) + logMsg("Validated we are not prelogged in for " + theServlet); + } else { + // ERROR - something was non-null which indicates that we are + // pre-logged in (e.g. pre-authenticated) for a scenario that does + // not require authentication. Either that or we are (correctly) + // not logged in *but* are incorrectly getting back non-null values + // when not logged in nor authenticated. + logMsg("Failed validation check for being pre-logged in."); + logMsg("doCheckForPreLogin(): request.getUserPrincipal() = null"); + rval = false; + } + } + + return rval; + } + + private void doServerCallbackChecks(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) { + + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + String servletPath = request.getContextPath() + request.getServletPath(); + String msg = ""; + + ServerCallbackSupport serverCallbacks = + new ServerCallbackSupport( + logger, callbackHandler, JASPICData.LAYER_SERVLET, messageInfo, + clientSubject, serviceSubject); + + // instead of calling all callbacks in one method, lets call them + // all individually so ithat we can check return values of each. + // serverCallbacks.verify(); + + if (serverCallbacks.verifyCPCCallback()) { + msg = "TSServerAuthModule.validateRequest(): verifyCPCCallback returned true"; + } else { + msg = "TSServerAuthModule.validateRequest(): verifyCPCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + if (serverCallbacks.verifyGPCCallback()) { + msg = "TSServerAuthModule.validateRequest(): verifyGPCCallback returned true"; + } else { + msg = "TSServerAuthModule.validateRequest(): verifyGPCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + if (serverCallbacks.verifyPVCCallback()) { + msg = "TSServerAuthModule.validateRequest(): verifyPVCCallback returned true"; + } else { + msg = "TSServerAuthModule.validateRequest(): verifyPVCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + return; + } + + /* + * This method will have some pre-canned status responses built into based on what the user name is. We will return + * status codes based on the passed in context info. + * + * One goal of this method is to cause a Callbackhandler to be invoked such that the Callbackhandler will return an + * AuthStatus code that matches our pre-canned (returned) AuthStatus. + */ + private AuthStatus getReturnStatus(MessageInfo msgInfo, Subject subject) throws AuthException { + AuthStatus rval = AuthStatus.SUCCESS; + String strStatus = "AuthStatus.SUCCESS"; + String msg; + int statusCode = 200; + + // get request object + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + // get name of servlet(servletPath) and not the app (eg context path) + String servletName = request.getServletPath(); + + msg = "HttpServlet profile with servletName=" + servletName; + if (servletName.contains(JASPICData.AUTHSTAT_SENDFAILURE_ND)) { + rval = AuthStatus.SEND_FAILURE; + strStatus = "AuthStatus.SEND_FAILURE"; + statusCode = 500; + + } else if (servletName.contains(JASPICData.AUTHSTAT_SENDCONT_ND)) { + rval = AuthStatus.SEND_CONTINUE; + strStatus = "AuthStatus.SEND_CONTINUE"; + statusCode = 401; + + } else if (servletName.contains(JASPICData.AUTHSTAT_SENDSUCCESS_ND)) { + rval = AuthStatus.SEND_SUCCESS; + strStatus = "AuthStatus.SEND_SUCCESS"; + statusCode = 200; + + } else if (servletName.contains(JASPICData.AUTHSTAT_FAILURE_ND)) { + rval = AuthStatus.FAILURE; + strStatus = "AuthStatus.FAILURE"; + statusCode = 500; + + } else if (servletName.contains(JASPICData.AUTHSTAT_THROW_EX_ND)) { + msg += " returning AuthStatus=AuthException"; + logger.log(INFO, msg); + throw new AuthException(msg); + + } else if (servletName.contains(JASPICData.AUTHSTAT_SUCCESS_ND) || servletName.contains(JASPICData.AUTHSTAT_OPT_SUCCESS) + || servletName.contains(JASPICData.AUTHSTAT_MAND_SUCCESS)) { + rval = AuthStatus.SUCCESS; + strStatus = "AuthStatus.SUCCESS"; + } + + msg += " returning AuthStatus=" + strStatus; + logger.log(INFO, msg); + + // lets get our response message in case we need to do something... + Object respObj = msgInfo.getResponseMessage(); + HttpServletResponseWrapper response = null; + if (respObj != null) { + if (respObj instanceof HttpServletResponseWrapper) { + if (response != null) { + response.setStatus(statusCode); + msgInfo.setResponseMessage(response); + } + } else if (respObj instanceof HttpServletResponse) { + response = new HttpServletResponseWrapper((HttpServletResponse) respObj); + response.setStatus(statusCode); + msgInfo.setResponseMessage(response); + } else { + msg = "WARNING: we have some unidentified response object."; + logger.log(INFO, msg); + } + } + + return rval; + } + + /* + * This is a convenience method that will likely only be called once. Currently there is only one key to dump from the + * map, but his may change in the future. + */ + private void dumpServletProfileKeys(MessageInfo msgInfo, String callerMethod) { + Map map = msgInfo.getMap(); + + // lets pull out some context info that we can use to help uniquely + // identify the source of this request + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + String servletName = request.getServletPath(); + + // see assertion JASPI:SPEC:306 for details on this + // Jakarta Authentication states the following key must exist for servlet profile + if (map != null) { + Set keys = map.keySet(); + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + Object oKey = iterator.next(); + if (oKey instanceof String) { + String key = (String) oKey; + if (key != null) { + Object obj = map.get(key); + if (obj instanceof String) { + String keyVal = (String) map.get(key); + String msg = "dumpServletProfileKeys() called with attrs: "; + msg += " layer=" + JASPICData.LAYER_SERVLET; + msg += " servletName=" + servletName; + msg += " callerMethod=" + callerMethod; + msg += " key=" + key; + + if (keyVal == null) { + msg += " value=NULL"; + } else if (Boolean.valueOf(keyVal).booleanValue() == true) { + msg += " value=Valid"; + } else { + msg += " value=Invalid value of: " + keyVal; + } + logger.log(INFO, msg); + } else { + logger.log(INFO, "Map key is of type :" + obj.getClass().getName()); + } + } + } + } + } + + } + + private boolean isAuthMandatory(MessageInfo msgInfo) { + boolean bval = false; + Map map = msgInfo.getMap(); + + // lets pull out some context info that we can use to help uniquely + // identify the source of this request + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + String servletName = request.getServletPath(); + + // see assertion JASPI:SPEC:306 for details on this + // jsr-196 states the following key must exist for servlet profile + String strKey = "jakarta.security.auth.message.MessagePolicy.isMandatory"; + String msg; + if (map != null) { + String keyVal = (String) map.get(strKey); + msg = "isAuthMandatory() called with attrs: "; + msg += " layer=" + JASPICData.LAYER_SERVLET; + msg += " servletName=" + servletName; + msg += " key=" + strKey; + + if (keyVal == null) { + msg += " value=NULL"; + bval = false; // assume false if we cant determine + } else if (Boolean.valueOf(keyVal).booleanValue() == true) { + msg += " value=Valid"; + bval = true; + } else { + // assume false + msg += " value=false"; + bval = false; + } + logger.log(Level.FINE, msg); + } else { + msg = "FAILURE: No map in MessageInfo thus no key=" + strKey; + logger.log(Level.SEVERE, msg); + } + + return bval; + } + + /* + * Convenience method to dump messageInfo logging out. + */ + private void logMessageTypes(MessageInfo messageInfo, String methodName) { + String msg; + + String requestURI = ""; + + if (messageInfo != null) { + Object reqObj = messageInfo.getRequestMessage(); + if (reqObj != null) { + // if here, we want to see if our reqObj is type HttpServletRequest + // and if so print out a log msg stating so. + // (Jakarta Authentication expects the reqObj to be type HttpServletRequest) + msg = methodName + ": MessageInfo.getRequestMessage() is of type "; + if (reqObj instanceof HttpServletRequest) { + msg = msg + "jakarta.servlet.http.HttpServletRequest"; + requestURI = ((HttpServletRequest) reqObj).getRequestURI(); + + // related to assertion JASPI:SPEC:95 , this block of code + // may be needed if we need to identify which servlet invoked this + if (requestURI != null) { + // we want to know which servlet/jsp/html page generated + // this action and we want to include that in the log so we + // can check if the log gets generated for both servlets and + // for static html - which is a jsr-196 requirement. + String msg2 = msg + " for requestURI=" + requestURI; + logMsg(msg2); + } + } else { + msg = msg + messageInfo.getClass().getName(); + } + logMsg(msg); + } + + Object respObj = messageInfo.getResponseMessage(); + if (respObj != null) { + // if here, we want to see if our respObj is type HttpServletResponse + // and if so print out a log msg stating so. (jsr-196 expects the + // respObj to be type HttpServletResponse) + msg = methodName + ": MessageInfo.getResponseMessage() is of type "; + if (respObj instanceof jakarta.servlet.http.HttpServletResponse) { + msg = msg + "jakarta.servlet.http.HttpServletResponse"; + } else { + msg = msg + messageInfo.getClass().getName(); + } + logMsg(msg); + } + + } else { + msg = "TSServerAuthModule." + methodName + " called with null MessageInfo object."; + logMsg(msg); + } + + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + logMsg("Enterred secureResponse"); + + String msg = ""; + if (serviceSubject != null) { + msg = "TSServerAuthModule.secureResponse called with serviceSubject :" + getPrincipalNameFromSubject(serviceSubject); + } else { + msg = "TSServerAuthModule.secureResponse called with null service Subject"; + } + logMsg(msg); + + logMessageTypes(messageInfo, "secureResponse"); + dumpServletProfileKeys(messageInfo, "secureResponse"); + + // verify that our context was not one that should have avoided a + // dispatch to service and thus should not have called secureResponse() + verifySecureResponseShouldHaveBeenCalled(messageInfo); + + // help verify assertion: JASPIC:SPEC:108 + setSpecialRequestAttribute(messageInfo, "secureRespCalled", "true"); + + return AuthStatus.SEND_SUCCESS; + } + + /* + * This is a specialized method used to set a request attribute that will be checked during the servlet/service + * invocation the idea is that secureResponse should not be called before the servlet/service invocation. + */ + public void setSpecialRequestAttribute(MessageInfo msgInfo, String key, String value) { + String msg; + + logMsg("TSServerAuthModule() setSpecialRequestAttribute called"); + + try { + // get request object + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + // we want the servletpath here (not the context path) as this + // will give us a better idea which servlet was invoked + String servletName = request.getServletPath(); + + if ((servletName.contains(JASPICData.AUTHSTAT_MAND_SUCCESS))) { + // lets set our cts proprietary request attr indicating + // flow was in secureResponse + logMsg("setSpecialRequestAttribute() called for servlet: " + servletName); + + request.setAttribute(key, value); + msgInfo.setRequestMessage(request); + logMsg("setSpecialRequestAttribute(): setAttribute() set for secureRespCalled=true"); + } + } catch (Exception ex) { + // should not get here but in case, dump the ex and return true + msg = "Got Unexpected Exception!"; + msg += " Exception message was: " + ex.toString(); + logMsg(msg + ""); + ex.printStackTrace(); + } + + } + + public void verifySecureResponseShouldHaveBeenCalled(MessageInfo msgInfo) { + String msg; + + try { + // get request object + HttpServletRequest request = (HttpServletRequest) msgInfo.getRequestMessage(); + + // we want the servletpath here (not the context path) as this + // will give us a better idea which servlet was invoked + String servletName = request.getServletPath(); + + if ((servletName.contains(JASPICData.AUTHSTAT_SENDFAILURE_ND)) || (servletName.contains(JASPICData.AUTHSTAT_SENDSUCCESS_ND)) + || (servletName.contains(JASPICData.AUTHSTAT_FAILURE_ND)) || (servletName.contains(JASPICData.AUTHSTAT_THROW_EX_ND)) + || (servletName.contains(JASPICData.AUTHSTAT_SUCCESS_ND))) { + // we have a specific context indicating we should NOT have been + // dispatched to a service (ie should not be in secureResponse) + msg = "FAILURE: should not have been dispatched to secureResponse"; + logger.log(INFO, msg); + } + } catch (Exception ex) { + // should not get here but in case, dump the ex and return true + msg = "Got Unexpected Exception!"; + msg += " Exception message was: " + ex.getMessage(); + logger.log(INFO, msg); + ex.printStackTrace(); + } + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSServerAuthModule.cleanSubject called"); + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + public String getPrincipalNameFromSubject(Subject subject) { + String concatPrincipalName = ""; + + for (Principal principal : subject.getPrincipals()) { + concatPrincipalName += principal.getName(); + } + + return concatPrincipalName; + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServletWrapperSAM.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServletWrapperSAM.java new file mode 100644 index 0000000..095af3e --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/servlet/TSServletWrapperSAM.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.servlet; + +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.basic.sam.ServerCallbackSupport; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.module.ServerAuthModule; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +public class TSServletWrapperSAM implements ServerAuthModule { + private TSLogger logger; + + private static CallbackHandler callbackHandler; + + private Class[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class }; + + /** + * Creates a new instance of TSServletWrapperSAM + */ + public TSServletWrapperSAM() { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + logMsg("TSServletWrapperSAM() constructor called"); + } + + public TSServletWrapperSAM(TSLogger log) { + if (log != null) { + logger = log; + } else { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + } + logMsg("TSServletWrapperSAM(TSLogger) constructor called"); + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException { + if ((options != null) && options.get("TSLogger") != null) { + logger = (TSLogger) options.get("TSLogger"); + } + + callbackHandler = handler; + + // perform some checking to support assertion JASPI:SPEC:87 + verifyRequestPolicy(requestPolicy); + + logger.log(INFO, "CBH for HttpServlet supports type: " + handler.getClass().getName()); + } + + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + doServerCallbackChecks(messageInfo, clientSubject, serviceSubject); + + // Wrap the request - the resource to be invoked should get to see this + TSRequestWrapper tswrap = new TSRequestWrapper((HttpServletRequest) messageInfo.getRequestMessage()); + tswrap.setOptionsMap(messageInfo.getMap()); + messageInfo.setRequestMessage(tswrap); + + // Wrap the response - the resource to be invoked should get to see this + messageInfo.setResponseMessage(new TSResponseWrapper((HttpServletResponse) messageInfo.getResponseMessage())); + + return AuthStatus.SUCCESS; + } + + @Override + public Class[] getSupportedMessageTypes() { + return supportedMessageTypes; + } + + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + + // Unwrap the request + if (request instanceof TSRequestWrapper) { + messageInfo.setRequestMessage(((TSRequestWrapper) request).getRequest()); + } else { + logMsg("Incorrect request type : " + request.getClass().getName()); + } + + HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage(); + + if (response instanceof TSResponseWrapper) { + messageInfo.setResponseMessage(((TSResponseWrapper) response).getResponse()); + } else { + logMsg("Incorrect response type : " + response.getClass().getName()); + } + + return AuthStatus.SEND_SUCCESS; + } + + /* + * This is a convenience method that will do some verification on the request policy to see if it complies with + * assertion JASPI:SPEC:87. If there are any problems found, appropriate log statements will be made and searched for + * later on in the Client code. + */ + private void verifyRequestPolicy(MessagePolicy requestPolicy) { + + String errStr = "Layer=" + JASPICData.LAYER_SERVLET; + errStr += " requestPolicy=invalid in TSServletWrapperSAM.initialize()"; + + if (requestPolicy == null) { + // we should never have a null requestpolicy here + logger.log(Level.SEVERE, errStr); + } else { + MessagePolicy.TargetPolicy[] tp = requestPolicy.getTargetPolicies(); + if (tp.length < 1) { + // must return an array containing at least one TargetPolicy + logger.log(INFO, errStr); + } else { + for (int ii = 0; ii < tp.length; ii++) { + MessagePolicy.ProtectionPolicy pp = tp[ii].getProtectionPolicy(); + if ((pp != null) && (!isProtectionPolicyIDValid(pp.getID()))) { + String str = "Layer=" + JASPICData.LAYER_SERVLET; + str += " Invalid ProtectionPolicy.getID()"; + logger.log(INFO, str); + } + } + } + } + } + + /* + * (spec section 3.7.4) For servlet profile, calling the getID() method on the ProtectionPolicy must return one of the + * following values: ProtectionPolicy.AUTHENTICATE_SENDER ProtectionPolicy.AUTHENTICATE_CONTENT + */ + public boolean isProtectionPolicyIDValid(String strId) { + boolean bval = false; + + if ((strId.equals(MessagePolicy.ProtectionPolicy.AUTHENTICATE_CONTENT)) + || (strId.equals(MessagePolicy.ProtectionPolicy.AUTHENTICATE_SENDER))) { + bval = true; + } + + return bval; + } + + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSServletWrapperSAM.cleanSubject called"); + subject = null; + } + + private void logMsg(String str) { + if (logger != null) { + logger.log(INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + private void doServerCallbackChecks(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) { + + HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage(); + String servletPath = request.getContextPath() + request.getServletPath(); + String msg = ""; + + ServerCallbackSupport serverCallbacks = new ServerCallbackSupport(logger, callbackHandler, JASPICData.LAYER_SERVLET, messageInfo, + clientSubject, serviceSubject); + + // instead of calling all callbacks in one method, lets call them + // all individually so ithat we can check return values of each. + // serverCallbacks.verify(); + + if (serverCallbacks.verifyCPCCallback()) { + msg = "TSServletWrapperSAM.validateRequest(): verifyCPCCallback returned true"; + } else { + msg = "TSServletWrapperSAM.validateRequest(): verifyCPCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + if (serverCallbacks.verifyGPCCallback()) { + msg = "TSServletWrapperSAM.validateRequest(): verifyGPCCallback returned true"; + } else { + msg = "TSServletWrapperSAM.validateRequest(): verifyGPCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + if (serverCallbacks.verifyPVCCallback()) { + msg = "TSServletWrapperSAM.validateRequest(): verifyPVCCallback returned true"; + } else { + msg = "TSServletWrapperSAM.validateRequest(): verifyPVCCallback returned false"; + } + msg += " for servletPath = " + servletPath; + logMsg(msg); + + return; + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/ClientCallbackSupport.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/ClientCallbackSupport.java new file mode 100644 index 0000000..d1f4d6f --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/ClientCallbackSupport.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import java.io.IOException; +import java.util.logging.Level; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * + * @author Raja Perumal + */ +public class ClientCallbackSupport { + private static TSLogger logger; + + private static CallbackHandler callbackHandler; + + private static String profile; + + private static final String runtimeType = "ClientRuntime"; + + /** Creates a new instance of ClientCallbackSupport */ + public ClientCallbackSupport(TSLogger tsLogger, CallbackHandler cbkHandler, String profile) { + logger = tsLogger; + callbackHandler = cbkHandler; + this.profile = profile; + } + + public boolean verify() { + try { + NameCallbackSupport(); + PasswordCallbackSupport(); + return true; + + } catch (Exception e) { + return false; + } + } + + private void NameCallbackSupport() { + if (callbackHandler != null) { + try { + NameCallback nameCallback = new NameCallback("Please enter your name :", "j2ee"); + nameCallback.setName("j2ee"); + + Callback[] callbacks = new Callback[] { nameCallback }; + + callbackHandler.handle(callbacks); + String returnedName = nameCallback.getName(); + + if (returnedName != null) { + logMsg("Name returned from Name Callback =" + returnedName); + } + logMsg("CallbackHandler supports NameCallback"); + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support NameCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support NameCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + } + + } + + private void PasswordCallbackSupport() { + if (callbackHandler != null) { + try { + PasswordCallback passwordCallback = new PasswordCallback("Please enter your password :", false); + passwordCallback.setPassword(new char[] { 'j', '2', 'e', 'e' }); + + Callback[] callbacks = new Callback[] { passwordCallback }; + + callbackHandler.handle(callbacks); + char returnedPassword[] = passwordCallback.getPassword(); + + if (returnedPassword != null) { + logMsg("Password returned from Password Callback =" + new String(returnedPassword)); + } + logMsg("CallbackHandler supports PasswordCallback"); + } catch (UnsupportedCallbackException usce) { + logMsg("CallbackHandler failed to support PasswordCallback :" + usce.getMessage()); + usce.printStackTrace(); + } catch (IOException ioe) { + logMsg("CallbackHandler failed to support PasswordCallback :" + ioe.getMessage()); + ioe.printStackTrace(); + } + + } + + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, "In " + profile + " : " + runtimeType + " " + str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionClientAuthModule.java new file mode 100644 index 0000000..86b947f --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionClientAuthModule.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSAuthExceptionClientAuthModule implements jakarta.security.auth.message.module.ClientAuthModule { + private static TSLogger logger; + + private static Map options; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSAuthExceptionClientAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + logMsg("TSAuthExceptionClientAuthModule.getSupportedMessageTypes called"); + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + logMsg("TSAuthExceptionClientAuthModule.secureRequest called"); + throw new AuthException("TSAuthExceptionClientAuthModule.secureRequest throws AuthException"); + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + logMsg("TSAuthExceptionClientAuthModule.validateResponse called"); + + throw new AuthException("TSAuthExceptionClientAuthModule.validateResponse throws AuthException"); + + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSAuthExceptionClientAuthModule.cleanSubject called"); + + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionServerAuthModule.java new file mode 100644 index 0000000..4a71c7f --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSAuthExceptionServerAuthModule.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.module.ServerAuthModule; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSAuthExceptionServerAuthModule implements ServerAuthModule { + private static TSLogger logger; + + private static Map options; + + /** + * Creates a new instance of TSAuthExceptionServerAuthModule + */ + public TSAuthExceptionServerAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSAuthExceptionServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + logMsg("TSAuthExceptionServerAuthModule.validateRequest called"); + + return AuthStatus.SUCCESS; + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + logMsg("TSAuthExceptionServerAuthModule.secureResponse called"); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSAuthExceptionServerAuthModule.cleanSubject called"); + + // Remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSClientAuthModule.java new file mode 100644 index 0000000..769b85d --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSClientAuthModule.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.basic.sam.CommonCallbackSupport; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.module.ClientAuthModule; +import java.security.Principal; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.xml.namespace.QName; + +/** + * + * @author Raja Perumal + */ +public class TSClientAuthModule implements ClientAuthModule { + private static TSLogger logger; + + private static CallbackHandler callbackHandler; + private static Map options; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSClientAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + callbackHandler = handler; + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + logMsg("TSClientAuthModule.getSupportedMessageTypes called"); + + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + + String msg = ""; + if (clientSubject != null) { + msg = "TSClientAuthModule.secureRequest called with client Subject :" + getPrincipalNameFromSubject(clientSubject); + } else + msg = "TSClientAuthModule.secureRequest called with null client Subject"; + + logMsg(msg); + logMessageTypes(messageInfo, "secureRequest"); + + // Check Callback Handler support for SOAP Client runtime + ClientCallbackSupport clientCallbackSupport = new ClientCallbackSupport(logger, callbackHandler, "SOAP"); + + clientCallbackSupport.verify(); + + // Check CommonCallbacks support for SOAP client runtime + CommonCallbackSupport commonCallbacks = new CommonCallbackSupport(logger, callbackHandler, "SOAP", "ClientRuntime"); + + commonCallbacks.verify(); + + // Log the value for key jakarta.xml.ws.wsdl.service in messageInfoMap + Map messageInfoMap = messageInfo.getMap(); + + QName qName = (QName) messageInfoMap.get("jakarta.xml.ws.wsdl.service"); + + if (qName != null) { + String qNameToString = qName.toString(); + + msg = "TSClientAuthModule.secureRequest messageInfo :" + "jakarta.xml.ws.wsdl.service=" + qNameToString; + } else { + msg = "TSClientAuthModule.secureRequest messageInfo :" + + "** ERROR ** No value found for key jakarta.xml.ws.wsdl.service in MessageInfoMap" + " : Expected a QName"; + + } + logMsg(msg); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + String msg = ""; + if (clientSubject != null) { + msg = "TSClientAuthModule.validateResponse called with client Subject :" + getPrincipalNameFromSubject(clientSubject); + } else + msg = "TSClientAuthModule.validateResponse called with null client Subject"; + + if (serviceSubject != null) { + msg = msg + " with serviceSubject :" + getPrincipalNameFromSubject(serviceSubject); + } else + msg = msg + " with null serviceSubject"; + + logMsg(msg); + logMessageTypes(messageInfo, "validateResponse"); + + // Log the value for key jakarta.xml.ws.wsdl.service in messageInfoMap + Map messageInfoMap = messageInfo.getMap(); + + QName qName = (QName) messageInfoMap.get("jakarta.xml.ws.wsdl.service"); + + if (qName != null) { + String qNameToString = qName.toString(); + + msg = "TSClientAuthModule.validateResponse messageInfo :" + "jakarta.xml.ws.wsdl.service=" + qNameToString; + } else { + msg = "TSClientAuthModule.validateResponse messageInfo :" + + "** ERROR ** No value found for key jakarta.xml.ws.wsdl.service in MessageInfoMap" + " : Expected a QName "; + + } + logMsg(msg); + + return AuthStatus.SUCCESS; + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSClientAuthModule.cleanSubject called"); + + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + private String getPrincipalNameFromSubject(Subject sub) { + String concatPrincipalName = ""; + + for (Principal principal : sub.getPrincipals()) { + concatPrincipalName += principal.getName(); + } + + return concatPrincipalName; + } + + private void logMessageTypes(MessageInfo messageInfo, String methodName) { + String msg = null; + + Object requestMessage = messageInfo.getRequestMessage(); + Object responseMessage = messageInfo.getResponseMessage(); + + if (requestMessage != null) { + if (requestMessage instanceof jakarta.xml.soap.SOAPMessage) { + msg = methodName + " : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage"; + logMsg(msg); + } else { + msg = methodName + " : MessageInfo.getRequestMessage() is of type " + requestMessage.getClass().getName(); + logMsg(msg); + + } + } + + if (responseMessage != null) { + if (responseMessage instanceof jakarta.xml.soap.SOAPMessage) { + msg = methodName + " : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage"; + logMsg(msg); + } else { + msg = methodName + " : MessageInfo.getResponseMessage() is of type " + responseMessage.getClass().getName(); + logMsg(msg); + + } + } + + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureClientAuthModule.java new file mode 100644 index 0000000..ee2caa9 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureClientAuthModule.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.xml.soap.MessageFactory; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.xml.namespace.QName; + +/** + * + * @author Raja Perumal + */ +public class TSFailureClientAuthModule implements jakarta.security.auth.message.module.ClientAuthModule { + private static TSLogger logger = null; + + private static Map options = null; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSFailureClientAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + logMsg("TSFailureClientAuthModule.getSupportedMessageTypes called"); + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + + String msg = "TSFailureClientAuthModule.secureRequest called"; + logMsg(msg); + SOAPMessage smsg = null; + + try { + + MessageFactory mf = MessageFactory.newInstance(); + smsg = mf.createMessage(); + if (smsg != null) { + setSOAPFault(smsg); + } + + } catch (SOAPException ex) { + ex.printStackTrace(); + } + + // set the response message with SOAP Fault + messageInfo.setResponseMessage(smsg); + + return AuthStatus.FAILURE; + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + String msg = "TSFailureClientAuthModule.validateResponse called"; + logMsg(msg); + + SOAPMessage smsg = (SOAPMessage) messageInfo.getResponseMessage(); + if (smsg != null) { + setSOAPFault(smsg); + } + + return AuthStatus.FAILURE; + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSFailureClientAuthModule.cleanSubject called"); + + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + private void setSOAPFault(SOAPMessage smsg) { + SOAPPart soap = smsg.getSOAPPart(); + if (soap != null) { + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope != null) { + SOAPBody body = envelope.getBody(); + if (body != null) { + QName qname = new QName("Client"); + body.addFault(qname, "Error in Client"); + } + } + } catch (SOAPException se) { + logger.log(Level.INFO, "Error adding SOAP Fault", se); + + } + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureServerAuthModule.java new file mode 100644 index 0000000..937ab4b --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSFailureServerAuthModule.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSFailureServerAuthModule implements jakarta.security.auth.message.module.ServerAuthModule { + private static TSLogger logger; + + private static Map options = null; + + /** + * Creates a new instance of TSFailureServerAuthModule + */ + public TSFailureServerAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSFailureServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + + String msg = "TSFailureServerAuthModule.validateRequest called"; + logMsg(msg); + + return AuthStatus.SUCCESS; + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + String msg = "TSFailureServerAuthModule.secureResponse called"; + logMsg(msg); + return AuthStatus.SEND_SUCCESS; + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSFailureServerAuthModule.cleanSubject called"); + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureClientAuthModule.java new file mode 100644 index 0000000..0a8a6d4 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureClientAuthModule.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPFault; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSSendFailureClientAuthModule implements jakarta.security.auth.message.module.ClientAuthModule { + private static TSLogger logger = null; + + private static Map options = null; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSSendFailureClientAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + logMsg("TSSendFailureClientAuthModule.getSupportedMessageTypes called"); + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + + String msg = "TSSendFailureClientAuthModule.secureRequest called"; + logMsg(msg); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + String msg = "TSSendFailureClientAuthModule.validateResponse called"; + logMsg(msg); + + SOAPMessage smsg = (SOAPMessage) messageInfo.getResponseMessage(); + String faultString = getSOAPFaultString(smsg); + if (faultString != null) { + msg = "TSSendFailureClientAuthModule.validateResponse received SOAPFault :" + faultString; + logMsg(msg); + } + return AuthStatus.SUCCESS; + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSSendFailureClientAuthModule.cleanSubject called"); + + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + private String getSOAPFaultString(SOAPMessage smsg) { + String faultString = null; + SOAPPart soap = smsg.getSOAPPart(); + if (soap != null) { + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope != null) { + SOAPBody body = envelope.getBody(); + if (body != null) { + if (body.hasFault()) { + SOAPFault sf = body.getFault(); + faultString = sf.getFaultString(); + } + } + } + } catch (SOAPException se) { + logger.log(Level.INFO, "Error adding SOAP Fault", se); + + } + } + return faultString; + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureServerAuthModule.java new file mode 100644 index 0000000..5d74ace --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendFailureServerAuthModule.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.xml.soap.MessageFactory; +import jakarta.xml.soap.SOAPBody; +import jakarta.xml.soap.SOAPEnvelope; +import jakarta.xml.soap.SOAPException; +import jakarta.xml.soap.SOAPMessage; +import jakarta.xml.soap.SOAPPart; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.xml.namespace.QName; + +/** + * + * @author Raja Perumal + */ +public class TSSendFailureServerAuthModule implements jakarta.security.auth.message.module.ServerAuthModule { + private static TSLogger logger = null; + + private static Map options = null; + + /** + * Creates a new instance of TSSendFailureServerAuthModule + */ + public TSSendFailureServerAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSSendFailureServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + + String msg = "TSSendFailureServerAuthModule.validateRequest called"; + logMsg(msg); + + SOAPMessage smsg = null; + + try { + + MessageFactory mf = MessageFactory.newInstance(); + smsg = mf.createMessage(); + if (smsg != null) { + setSOAPFault(smsg); + } + + } catch (SOAPException ex) { + ex.printStackTrace(); + } + + // set the response message with SOAP Fault + messageInfo.setResponseMessage(smsg); + + return AuthStatus.SEND_FAILURE; + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + String msg = "TSSendFailureServerAuthModule.secureResponse called"; + logMsg(msg); + + SOAPMessage smsg = null; + + try { + + MessageFactory mf = MessageFactory.newInstance(); + smsg = mf.createMessage(); + if (smsg != null) { + setSOAPFault(smsg); + } + + } catch (SOAPException ex) { + ex.printStackTrace(); + } + + // set the response message with SOAP Fault + messageInfo.setResponseMessage(smsg); + + return AuthStatus.SEND_FAILURE; + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSSendFailureServerAuthModule.cleanSubject called"); + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + private void setSOAPFault(SOAPMessage smsg) { + SOAPPart soap = smsg.getSOAPPart(); + if (soap != null) { + try { + SOAPEnvelope envelope = soap.getEnvelope(); + if (envelope != null) { + SOAPBody body = envelope.getBody(); + if (body != null) { + QName qname = new QName("Server"); + body.addFault(qname, "Error in Server"); + + } + } + } catch (SOAPException se) { + logger.log(Level.INFO, "Error adding SOAP Fault", se); + + } + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessClientAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessClientAuthModule.java new file mode 100644 index 0000000..390f457 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessClientAuthModule.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSSendSuccessClientAuthModule implements jakarta.security.auth.message.module.ClientAuthModule { + private static TSLogger logger = null; + + private static Map options = null; + + /** + * Creates a new instance of ClientAuthModuleImpl + */ + public TSSendSuccessClientAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects where each element defines a message type supported by the module. A module should + * return an array containing at least one element. An empty array indicates that the module will attempt to support any + * message type. This method never returns null. + */ + @Override + public Class[] getSupportedMessageTypes() { + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + logMsg("TSSendSuccessClientAuthModule.getSupportedMessageTypes called"); + return classarray; + } + + /** + * Secure a service request message before sending it to the service. + *

+ * This method is called to transform the request message acquired by calling getRequestMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to secure the request. If the Subject is + * not null, the method implementation may add additional Principals or credentials (pertaining to the source of the + * service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully secured. The secured request message may + * be obtained by calling by getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application request message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response from the peer security system. This status value + * also indicates that the application message has not yet been secured. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange after having received the security-specific response elicited by sending the security message. + * + * When this status value is returned, the corresponding invocation of validateResponse must be able to + * obtain the original application request message. + * + *
  • AuthStatus.FAILURE to indicate that a failure occured while securing the request message, and that an appropriate + * failure response message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureRequest(MessageInfo messageInfo, Subject clientSubject) throws AuthException { + + String msg = "TSSendSuccessClientAuthModule.secureRequest called"; + logMsg(msg); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Validate a received service response. + *

+ * This method is called to transform the mechanism specific response message acquired by calling getResponseMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the response + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into the next mechanism specific request message to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the recipient of the service response, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the response. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + *

    + *
  • AuthStatus.SUCCESS when the application response message was successfully validated. The validated message is + * available by calling getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that response validation is incomplete, and that a continuation request was + * returned as the request message within messageInfo. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by conducting at least one additional request/response + * exchange. + * + *
  • AuthStatus.FAILURE to indicate that validation of the response failed, and that a failure response message has + * been established in messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateResponse(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + String msg = "TSSendSuccessClientAuthModule.validateResponse called"; + logMsg(msg); + + return AuthStatus.SUCCESS; + } + + /** + * Remove implementation specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSSendSuccessClientAuthModule.cleanSubject called"); + + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessServerAuthModule.java new file mode 100644 index 0000000..ac0bdd1 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSSendSuccessServerAuthModule.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.util.Map; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSSendSuccessServerAuthModule implements jakarta.security.auth.message.module.ServerAuthModule { + private static TSLogger logger = null; + + private static Map options = null; + + /** + * Creates a new instance of TSSendSuccessServerAuthModule + */ + public TSSendSuccessServerAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSSendSuccessServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + + String msg = "TSSendSuccessServerAuthModule.validateRequest called"; + logMsg(msg); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + String msg = "TSSendSuccessServerAuthModule.secureResponse called"; + logMsg(msg); + return AuthStatus.SEND_SUCCESS; + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSSendSuccessServerAuthModule.cleanSubject called"); + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSServerAuthModule.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSServerAuthModule.java new file mode 100644 index 0000000..d3c5e41 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/module/soap/TSServerAuthModule.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.module.soap; + +import ee.jakarta.tck.authentication.test.basic.sam.CommonCallbackSupport; +import ee.jakarta.tck.authentication.test.basic.sam.ServerCallbackSupport; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import java.security.Principal; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +/** + * + * @author Raja Perumal + */ +public class TSServerAuthModule implements jakarta.security.auth.message.module.ServerAuthModule { + private static TSLogger logger; + + private static CallbackHandler callbackHandler; + + private static Map options; + + /** + * Creates a new instance of TSServerAuthModule + */ + public TSServerAuthModule() { + } + + /** + * Initialize this module with request and response message policies to enforce, a CallbackHandler, and any + * module-specific configuration properties. + * + *

+ * The request policy and the response policy must not both be null. + * + * @param requestPolicy the request policy this module must enforce, or null. + * + * @param responsePolicy the response policy this module must enforce, or null. + * + * @param handler CallbackHandler used to request information. + * + * @param options a Map of module-specific configuration properties. + * + * @exception AuthException if module initialization fails, including for the case where the options argument contains + * elements that are not supported by the module. + */ + @Override + public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy, CallbackHandler handler, Map optns) throws AuthException { + callbackHandler = handler; + options = optns; + + // Get the reference to TSLogger from the Map "options" + if (options.get("TSLogger") != null) + logger = (TSLogger) options.get("TSLogger"); + + } + + /** + * Get the one or more Class objects representing the message types supported by the module. + * + * @return an array of Class objects, with at least one element defining a message type supported by the module. + */ + @Override + public Class[] getSupportedMessageTypes() { + logMsg("TSServerAuthModule.getSupportedMessageTypes called"); + Class[] classarray = { jakarta.xml.soap.SOAPMessage.class }; + return classarray; + } + + /** + * Authenticate a received service request. + * + * This method is called to transform the mechanism specific request message acquired by calling getRequestMessage (on + * messageInfo) into the validated application message to be returned to the message processing runtime. If the received + * message is a (mechanism specific) meta-message, the method implementation must attempt to transform the meta-message + * into a corresponding mechanism specific response message, or to the validated application request message. The + * runtime will bind a validated application message into the the corresponding service invocation. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param clientSubject a Subject that represents the source of the service request. It is used by the method + * implementation to store Principals and credentials validated in the request. + * + * @param serviceSubject a Subject that represents the recipient of the service request, or null. It may be used by the + * method implementation as the source of Principals or credentials to be used to validate the request. If the Subject + * is not null, the method implementation may add additional Principals or credentials (pertaining to the recipient of + * the service request) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SUCCESS when the application request message was successfully validated. The validated request message + * is available by calling getRequestMessage on messageInfo. + * + *
  • AuthStatus.SEND_SUCCESS to indicate that validation/processing of the request message successfully produced the + * secured application response message (in messageInfo). The secured response message is available by calling + * getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that message validation is incomplete, and that a preliminary response was + * returned as the response message in messageInfo. + * + * When this status value is returned to challenge an application request message, the challenged request must be saved + * by the authentication module such that it can be recovered when the module's validateRequest message is called to + * process the request returned for the challenge. + * + *
  • AuthStatus.SEND_FAILURE to indicate that message validation failed and that an appropriate failure response + * message is available by calling getResponseMessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { + + String msg = ""; + if (clientSubject != null) { + msg = "TSServerAuthModule.validateRequest called with client Subject :" + getPrincipalNameFromSubject(clientSubject); + } else + msg = "TSClientAuthModule.validateRequest called with null client Subject"; + + if (serviceSubject != null) { + msg = msg + " with serviceSubject :" + getPrincipalNameFromSubject(serviceSubject); + } else + msg = msg + " with null serviceSubject"; + + logMsg(msg); + logMessageTypes(messageInfo, "validateRequest"); + + // Check Callback Handler support for Server runtime + ServerCallbackSupport serverCallbackSupport = new ServerCallbackSupport(logger, callbackHandler, "SOAP"); + + serverCallbackSupport.verify(); + + // Check common Callback support for SOAP server runtime + CommonCallbackSupport commonCallbacks = new CommonCallbackSupport(logger, callbackHandler, "SOAP", "ServerRuntime"); + + commonCallbacks.verify(); + + return AuthStatus.SUCCESS; + } + + /** + * Secure a service response before sending it to the client. + * + * This method is called to transform the response message acquired by calling getResponseMessage (on messageInfo) into + * the mechanism specific form to be sent by the runtime. + *

+ * This method conveys the outcome of its message processing either by returning an AuthStatus value or by throwing an + * AuthException. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param serviceSubject a Subject that represents the source of the service response, or null. It may be used by the + * method implementation to retrieve Principals and credentials necessary to secure the response. If the Subject is not + * null, the method implementation may add additional Principals or credentials (pertaining to the source of the service + * response) to the Subject. + * + * @return an AuthStatus object representing the completion status of the processing performed by the method. The + * AuthStatus values that may be returned by this method are defined as follows: + * + *

    + *
  • AuthStatus.SEND_SUCCESS when the application response message was successfully secured. The secured response + * message may be obtained by calling by getResponseMessage on messageInfo. + * + *
  • AuthStatus.SEND_CONTINUE to indicate that the application response message (within messageInfo) was replaced with + * a security message that should elicit a security-specific response (in the form of a request) from the peer. + * + * This status value serves to inform the calling runtime that (in order to successfully complete the message exchange) + * it will need to be capable of continuing the message dialog by processing at least one additional request/response + * exchange (after having sent the response message returned in messageInfo). + * + * When this status value is returned, the application response must be saved by the authentication module such that it + * can be recovered when the module's validateRequest message is called to process the elicited response. + * + *
  • AuthStatus.SEND_FAILURE to indicate that a failure occured while securing the response message and that an + * appropriate failure response message is available by calling getResponseMeessage on messageInfo. + *
+ * + * @exception AuthException when the message processing failed without establishing a failure response message (in + * messageInfo). + */ + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { + String msg = ""; + if (serviceSubject != null) { + msg = "TSServerAuthModule.secureResponse called with serviceSubject :" + getPrincipalNameFromSubject(serviceSubject); + } else + msg = "TSServerAuthModule.secureResponse called with null service Subject"; + + logMsg(msg); + logMessageTypes(messageInfo, "secureResponse"); + + return AuthStatus.SEND_SUCCESS; + } + + /** + * Remove method specific principals and credentials from the subject. + * + * @param messageInfo a contextual object that encapsulates the client request and server response objects, and that may + * be used to save state across a sequence of calls made to the methods of this interface for the purpose of completing + * a secure message exchange. + * + * @param subject the Subject instance from which the Principals and credentials are to be removed. + * + * @exception AuthException if an error occurs during the Subject processing. + */ + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { + logMsg("TSServerAuthModule.cleanSubject called"); + // remove the contents of the subject and return an empty subject + subject = null; + } + + public void logMsg(String str) { + if (logger != null) { + logger.log(Level.INFO, str); + } else { + System.out.println("*** TSLogger Not Initialized properly ***"); + System.out.println("*** TSSVLogMessage : ***" + str); + } + } + + public String getPrincipalNameFromSubject(Subject sub) { + Principal principal = null; + String concatPrincipalName = ""; + Set principalSet = sub.getPrincipals(); + + Iterator iterator = principalSet.iterator(); + while (iterator.hasNext()) { + principal = (Principal) iterator.next(); + concatPrincipalName += principal.getName(); + } + + return concatPrincipalName; + } + + private void logMessageTypes(MessageInfo messageInfo, String methodName) { + String msg = null; + + Object requestMessage = messageInfo.getRequestMessage(); + Object responseMessage = messageInfo.getResponseMessage(); + + if (requestMessage != null) { + if (requestMessage instanceof jakarta.xml.soap.SOAPMessage) { + msg = methodName + " : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage"; + logMsg(msg); + } else { + msg = methodName + " : MessageInfo.getRequestMessage() is of type " + requestMessage.getClass().getName(); + logMsg(msg); + + } + } + + if (responseMessage != null) { + if (responseMessage instanceof jakarta.xml.soap.SOAPMessage) { + msg = methodName + " : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage"; + logMsg(msg); + } else { + msg = methodName + " : MessageInfo.getResponseMessage() is of type " + responseMessage.getClass().getName(); + logMsg(msg); + + } + } + + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/BASE64Decoder.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/BASE64Decoder.java new file mode 100644 index 0000000..f233174 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/BASE64Decoder.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1995, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.util; + +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.io.PrintStream; + +/** + * This class implements a BASE64 Character decoder as specified in RFC1521. + * + * This RFC is part of the MIME specification which is published by the Internet + * Engineering Task Force (IETF). Unlike some other encoding schemes there is + * nothing in this encoding that tells the decoder where a buffer starts or + * stops, so to use it you will need to isolate your encoded data into a single + * chunk and then feed them this decoder. The simplest way to do that is to read + * all of the encoded data into a string and then use: + * + *
+ * byte mydata[];
+ * BASE64Decoder base64 = new BASE64Decoder();
+ *
+ * mydata = base64.decodeBuffer(bufferString);
+ * 
+ * + * This will decode the String in bufferString and give you an array of + * bytes in the array myData. + * + * On errors, this class throws a CEFormatException with the following detail + * strings: + * + *
+ * "BASE64Decoder: Not enough bytes for an atom."
+ * 
+ * + * @author Chuck McManis + * @see CharacterEncoder + * @see BASE64Decoder + */ + +public class BASE64Decoder extends CharacterDecoder { + + /** This class has 4 bytes per atom */ + protected int bytesPerAtom() { + return (4); + } + + /** Any multiple of 4 will do, 72 might be common */ + protected int bytesPerLine() { + return (72); + } + + /** + * This character array provides the character to value map based on RFC1521. + */ + private final static char pem_array[] = { + // 0 1 2 3 4 5 6 7 + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6 + '4', '5', '6', '7', '8', '9', '+', '/' // 7 + }; + + private final static byte pem_convert_array[] = new byte[256]; + + static { + for (int i = 0; i < 255; i++) { + pem_convert_array[i] = -1; + } + for (int i = 0; i < pem_array.length; i++) { + pem_convert_array[pem_array[i]] = (byte) i; + } + } + + byte decode_buffer[] = new byte[4]; + + /** + * Decode one BASE64 atom into 1, 2, or 3 bytes of data. + */ + protected void decodeAtom(PushbackInputStream inStream, + OutputStream outStream, int rem) throws java.io.IOException { + int i; + byte a = -1, b = -1, c = -1, d = -1; + + if (rem < 2) { + throw new CEFormatException( + "BASE64Decoder: Not enough bytes for an atom."); + } + do { + i = inStream.read(); + if (i == -1) { + throw new CEStreamExhausted(); + } + } while (i == '\n' || i == '\r'); + decode_buffer[0] = (byte) i; + + i = readFully(inStream, decode_buffer, 1, rem - 1); + if (i == -1) { + throw new CEStreamExhausted(); + } + + if (rem > 3 && decode_buffer[3] == '=') { + rem = 3; + } + if (rem > 2 && decode_buffer[2] == '=') { + rem = 2; + } + switch (rem) { + case 4: + d = pem_convert_array[decode_buffer[3] & 0xff]; + // NOBREAK + case 3: + c = pem_convert_array[decode_buffer[2] & 0xff]; + // NOBREAK + case 2: + b = pem_convert_array[decode_buffer[1] & 0xff]; + a = pem_convert_array[decode_buffer[0] & 0xff]; + break; + } + + switch (rem) { + case 2: + outStream.write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3))); + break; + case 3: + outStream.write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3))); + outStream.write((byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf))); + break; + case 4: + outStream.write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3))); + outStream.write((byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf))); + outStream.write((byte) (((c << 6) & 0xc0) | (d & 0x3f))); + break; + } + return; + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEFormatException.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEFormatException.java new file mode 100644 index 0000000..2a52316 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEFormatException.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 1995, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.util; + +import java.io.IOException; + +public class CEFormatException extends IOException { + public CEFormatException(String s) { + super(s); + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEStreamExhausted.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEStreamExhausted.java new file mode 100644 index 0000000..f44dd79 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CEStreamExhausted.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 1995, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.util; + +import java.io.IOException; + +/** This exception is thrown when EOF is reached */ +public class CEStreamExhausted extends IOException { +}; diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CharacterDecoder.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CharacterDecoder.java new file mode 100644 index 0000000..d3ed788 --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/sam/util/CharacterDecoder.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 1995, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.sam.util; + +import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * This class defines the decoding half of character encoders. A character + * decoder is an algorithim for transforming 8 bit binary data that has been + * encoded into text by a character encoder, back into original binary form. + * + * The character encoders, in general, have been structured around a central + * theme that binary data can be encoded into text that has the form: + * + *
+ *      [Buffer Prefix]
+ *      [Line Prefix][encoded data atoms][Line Suffix]
+ *      [Buffer Suffix]
+ * 
+ * + * Of course in the simplest encoding schemes, the buffer has no distinct prefix + * of suffix, however all have some fixed relationship between the text in an + * 'atom' and the binary data itself. + * + * In the CharacterEncoder and CharacterDecoder classes, one complete chunk of + * data is referred to as a buffer. Encoded buffers are all text, and + * decoded buffers (sometimes just referred to as buffers) are binary octets. + * + * To create a custom decoder, you must, at a minimum, overide three abstract + * methods in this class. + *
+ *
bytesPerAtom which tells the decoder how many bytes to expect from + * decodeAtom + *
decodeAtom which decodes the bytes sent to it as text. + *
bytesPerLine which tells the encoder the maximum number of bytes per + * line. + *
+ * + * In general, the character decoders return error in the form of a + * CEFormatException. The syntax of the detail string is + * + *
+ *      DecoderClassName: Error message.
+ * 
+ * + * Several useful decoders have already been written and are referenced in the + * See Also list below. + * + * @author Chuck McManis + * @see CEFormatException + * @see CharacterEncoder + * @see UCDecoder + * @see UUDecoder + * @see BASE64Decoder + */ + +public abstract class CharacterDecoder { + + /** Return the number of bytes per atom of decoding */ + abstract protected int bytesPerAtom(); + + /** Return the maximum number of bytes that can be encoded per line */ + abstract protected int bytesPerLine(); + + /** decode the beginning of the buffer, by default this is a NOP. */ + protected void decodeBufferPrefix(PushbackInputStream aStream, + OutputStream bStream) throws IOException { + } + + /** decode the buffer suffix, again by default it is a NOP. */ + protected void decodeBufferSuffix(PushbackInputStream aStream, + OutputStream bStream) throws IOException { + } + + /** + * This method should return, if it knows, the number of bytes that will be + * decoded. Many formats such as uuencoding provide this information. By + * default we return the maximum bytes that could have been encoded on the + * line. + */ + protected int decodeLinePrefix(PushbackInputStream aStream, + OutputStream bStream) throws IOException { + return (bytesPerLine()); + } + + /** + * This method post processes the line, if there are error detection or + * correction codes in a line, they are generally processed by this method. + * The simplest version of this method looks for the (newline) character. + */ + protected void decodeLineSuffix(PushbackInputStream aStream, + OutputStream bStream) throws IOException { + } + + /** + * This method does an actual decode. It takes the decoded bytes and writes + * them to the OutputStream. The integer l tells the method how many + * bytes are required. This is always <= bytesPerAtom(). + */ + protected void decodeAtom(PushbackInputStream aStream, OutputStream bStream, + int l) throws IOException { + throw new CEStreamExhausted(); + } + + /** + * This method works around the bizarre semantics of BufferedInputStream's + * read method. + */ + protected int readFully(InputStream in, byte buffer[], int offset, int len) + throws java.io.IOException { + for (int i = 0; i < len; i++) { + int q = in.read(); + if (q == -1) + return ((i == 0) ? -1 : i); + buffer[i + offset] = (byte) q; + } + return len; + } + + /** + * Decode the text from the InputStream and write the decoded octets to the + * OutputStream. This method runs until the stream is exhausted. + * + * @exception CEFormatException + * An error has occured while decoding + * @exception CEStreamExhausted + * The input stream is unexpectedly out of data + */ + public void decodeBuffer(InputStream aStream, OutputStream bStream) + throws IOException { + int i; + int totalBytes = 0; + + PushbackInputStream ps = new PushbackInputStream(aStream); + decodeBufferPrefix(ps, bStream); + while (true) { + int length; + + try { + length = decodeLinePrefix(ps, bStream); + for (i = 0; (i + bytesPerAtom()) < length; i += bytesPerAtom()) { + decodeAtom(ps, bStream, bytesPerAtom()); + totalBytes += bytesPerAtom(); + } + if ((i + bytesPerAtom()) == length) { + decodeAtom(ps, bStream, bytesPerAtom()); + totalBytes += bytesPerAtom(); + } else { + decodeAtom(ps, bStream, length - i); + totalBytes += (length - i); + } + decodeLineSuffix(ps, bStream); + } catch (CEStreamExhausted e) { + break; + } + } + decodeBufferSuffix(ps, bStream); + } + + /** + * Alternate decode interface that takes a String containing the encoded + * buffer and returns a byte array containing the data. + * + * @exception CEFormatException + * An error has occured while decoding + */ + public byte decodeBuffer(String inputString)[] throws IOException { + byte inputBuffer[] = new byte[inputString.length()]; + ByteArrayInputStream inStream; + ByteArrayOutputStream outStream; + + inputString.getBytes(0, inputString.length(), inputBuffer, 0); + inStream = new ByteArrayInputStream(inputBuffer); + outStream = new ByteArrayOutputStream(); + decodeBuffer(inStream, outStream); + return (outStream.toByteArray()); + } + + /** + * Decode the contents of the inputstream into a buffer. + */ + public byte decodeBuffer(InputStream in)[] throws IOException { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + decodeBuffer(in, outStream); + return (outStream.toByteArray()); + } + + /** + * Decode the contents of the String into a ByteBuffer. + */ + public ByteBuffer decodeBufferToByteBuffer(String inputString) + throws IOException { + return ByteBuffer.wrap(decodeBuffer(inputString)); + } + + /** + * Decode the contents of the inputStream into a ByteBuffer. + */ + public ByteBuffer decodeBufferToByteBuffer(InputStream in) + throws IOException { + return ByteBuffer.wrap(decodeBuffer(in)); + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonTests.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonTests.java new file mode 100644 index 0000000..ec7910a --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonTests.java @@ -0,0 +1,1409 @@ +/* + * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactoryForStandalone; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigProviderStandalone; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSRegistrationListener; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +/** + * This contains generic methods/tests which are expected to be used by tests in each of the supported profiles (eg + * SERVLET, SOAP, Standalone, etc) + * + * @author Oracle + */ +public class CommonTests { + private static final String AuthConfigProviderClass = TSAuthConfigProviderStandalone.class.getName(); + private static final String AuthConfigProviderLayer = "STANDALONE_LAYER"; + private static final String AuthConfigProviderAppContext = "STANDALONE_CONTEXT"; + private static final String AuthConfigProviderDesc = "Some description"; + + private PrintWriter out; + + public CommonTests() { + + } + + public CommonTests(PrintWriter out) { + this.out = out; + } + + public void setOut(PrintWriter out) { + this.out = out; + } + + public PrintWriter getOut() { + return this.out; + } + + /* + * this tests the following: - get current ACF - verify that removeRegistration(arg) will return FALSE when invalid arg + * supplied. + */ + public void _ACF_testFactoryRemoveRegistration() throws Exception { + try { + // get current AuthConfigFactory + AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory(); + if (authConfigFactory == null) { + // this could fail because the system can't + // find AuthConfigFactory (check for setting of -Djava.security.properties) + printIt("FAILURE - Could not get current AuthConfigFactory."); + throw new Exception("_ACF_testFactoryRemoveRegistration : FAILED"); + } + + // based on javadoc, calling removeRegistration with invalid registration + // must return false. It must NOT return true nor throw exception. + boolean rval = authConfigFactory.removeRegistration("SomePhakeRegistrationThatWontExist"); + + if (rval == true) { + printIt("FAILED: calling removeRegistration() on invalid registration returned true."); + throw new Exception("_ACF_testFactoryRemoveRegistration : FAILED"); + } + } catch (Exception ex) { + printIt("FAILED: calling removeRegistration() with invalid registration threw exception."); + throw ex; + } + + printIt("_ACF_testFactoryRemoveRegistration : passed"); + } + + /* + * this tests the following: - get current ACF - verify that getRegistrationIDs(acp) NEVER returns null hint: must + * return empty array even if unrecognized acp. + */ + public void _ACF_testFactoryGetRegistrationIDs() throws Exception { + try { + // Get current AuthConfigFactory + AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory(); + if (authConfigFactory == null) { + // This could fail because the system can't find AuthConfigFactory + // (check for setting of -Djava.security.properties) + printIt("FAILURE - Could not get current AuthConfigFactory."); + throw new Exception("_ACF_testFactoryGetRegistrationIDs : FAILED"); + } + + String[] registrationIDs = authConfigFactory.getRegistrationIDs(null); + + if (registrationIDs == null) { + printIt("FAILED: calling getRegistrationIDs(null) returned null."); + throw new Exception("_ACF_testFactoryGetRegistrationIDs : FAILED"); + } + } catch (Exception ex) { + printIt("FAILED: calling getRegistrationIDs(null) threw exception."); + throw ex; + } + + printIt("_ACF_testFactoryGetRegistrationIDs : passed"); + } + + /* + * this tests the following: - get current ACF - verify getRegistrationContext(string) returns NULL for unrecognized + * string. + */ + public void _ACF_testFactoryGetRegistrationContext() throws Exception { + try { + // get current AuthConfigFactory + AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory(); + if (authConfigFactory == null) { + // This could fail due to permissions or because the system can't + // find ACF (check for setting of -Djava.security.properties) + printIt("FAILURE - Could not get current AuthConfigFactory."); + throw new Exception("_ACF_testFactoryGetRegistrationContext : FAILED"); + } + + AuthConfigFactory.RegistrationContext registrationContext = + authConfigFactory.getRegistrationContext("SomePhakeRegistrationThatWontExist"); + + if (registrationContext != null) { + printIt("FAILED: calling getRegistrationContext() on invalid context returned non-null."); + throw new Exception("_ACF_testFactoryGetRegistrationContext : FAILED"); + } + } catch (Exception ex) { + printIt("FAILED: calling getRegistrationContext() with invalid registration threw exception."); + throw ex; + } + + printIt("_ACF_testFactoryGetRegistrationContext : passed"); + } + + /* + * this tests the following: - get vendors ACF - verify detachListener() returns non-NULL for listener that is not found + * tied to ACP with the passed in layer & appContext. unrecognized string. + */ + public void _ACF_testFactoryDetachListener(String vendorsClassName) throws Exception { + + try { + // Change AuthConfigFactory to use vendors AuthConfigFactory instead of CTS AuthConfigFactory + printIt("getting Auth Config Factory for: " + vendorsClassName); + AuthConfigFactory vendorsAuthConfigFactory = CommonUtils.getAuthConfigFactory(vendorsClassName); + AuthConfigFactory.setFactory(vendorsAuthConfigFactory); + String vendorAuthConfigFactoryClass = vendorsAuthConfigFactory.getClass().getName(); + printIt("vendorACFClass = " + vendorAuthConfigFactoryClass); + + MyRegistrationListener listener = new MyRegistrationListener("phakeLayer", "phakeAppContext"); + String[] listeners = vendorsAuthConfigFactory.detachListener(listener, "phakeLayer", "phakeAppContext"); + + if (listeners == null) { + // should NOT get here since we expect non-null returned...it may be + // empty but should never be null + printIt("FAILED: calling detachListener() on unfound listener returned null."); + throw new Exception("_ACF_testFactoryDetachListener : FAILED"); + } + } catch (Exception ex) { + printIt("FAILED: calling detachListener() with unfound listener threw exception."); + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("_ACF_testFactoryDetachListener : passed"); + } + + /* + * this tests the following: - get current (CTS default) ACF - switch ACF to use differetn (CTS) ACF - verify newly set + * ACF is correctly recognized (via getFactory() calls) - rest ACF back to (CTS default)default + */ + public void _ACF_getFactory() throws Exception { + try { + // get current AuthConfigFactory + AuthConfigFactory currentAuthConfigFactory = AuthConfigFactory.getFactory(); + if (currentAuthConfigFactory == null) { + // This could fail due to permissions or because the system can't + // find AuthConfigFactory (check for setting of -Djava.security.properties) + printIt("FAILURE - Could not get current AuthConfigFactory."); + throw new Exception("ACF_getFactory : FAILED"); + } + String currentACFClass = currentAuthConfigFactory.getClass().getName(); + printIt("ACF_getFactory.currentACFClass = " + currentACFClass); + + // Set our AuthConfigFactory to a new/different AuthConfigFactory + TSAuthConfigFactoryForStandalone newAcf = new TSAuthConfigFactoryForStandalone(); + AuthConfigFactory.setFactory(newAcf); + String newACFClass = newAcf.getClass().getName(); + printIt("ACF_getFactory.newACFClass = " + newACFClass); + + // verify our calls to getFactory() are getting the newly set factory + // instance and not the original ACF instance + AuthConfigFactory testAcf = AuthConfigFactory.getFactory(); + if (testAcf == null) { + printIt("FAILURE - Could not get newly set AuthConfigFactory."); + throw new Exception("ACF_getFactory : FAILED"); + } + String newlySetACFClass = testAcf.getClass().getName(); + printIt("ACF_getFactory.newlySetACFClass = " + newlySetACFClass); + + // Verify ACF's were set correctly + if (!newlySetACFClass.equals(newACFClass)) { + printIt("FAILURE - our current ACF does not match our newly set ACF"); + throw new Exception("ACF_getFactory : FAILED"); + } else { + String msg = "success - newlySetACFClass == newACFClass == " + newACFClass; + printIt(msg); + } + + // restore original factory class + AuthConfigFactory.setFactory(currentAuthConfigFactory); + + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory"; + msg = msg + "or ACF.setFactory(). Check your server side security policies."; + printIt(msg); + ex.printStackTrace(); + throw ex; + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("ACF_getFactory : passed"); + } + + /* + * This is more comprehensive than ACF_getFactory as this tests the following: - get current (CTS default) ACF - verify + * we are starting with correct default - switch ACF to use different (CTS) ACF - verify newly set ACF is correctly + * recognized by checking a few different things (ACF factory classname, etc... - rest ACF back to (CTS default)default + */ + public void _ACFSwitchFactorys(String vendorsClassName) throws Exception { + + try { + // get current (CTS) AuthConfigFactory + AuthConfigFactory ctsAuthConfigFactory = AuthConfigFactory.getFactory(); + if (ctsAuthConfigFactory == null) { + // This could fail because the system can't + // find AuthConfigFactory (check for setting of -Djava.security.properties) + printIt("FAILURE - Could not get current AuthConfigFactory."); + throw new Exception("ACFSwitchFactorys : FAILED"); + } + + // Verify we are starting with CTS AuthConfigFactory as the default AuthConfigFactory + String startingAuthConfigFactoryClass = ctsAuthConfigFactory.getClass().getName(); + printIt("startingACFClass = " + startingAuthConfigFactoryClass); + if (!JASPICData.TSSV_ACF.equals(startingAuthConfigFactoryClass)) { + printIt("ERROR: we are not starting with expected default ACF class of: " + JASPICData.TSSV_ACF); + throw new Exception("ACFSwitchFactorys : FAILED"); + } + + // Change AuthConfigFactory to use vendors AuthConfigFactory instead of CTS AuthConfigFactory + printIt("getting Auth Config for: " + vendorsClassName); + AuthConfigFactory vendorsAuthConfigFactory = CommonUtils.getAuthConfigFactory(vendorsClassName); + AuthConfigFactory.setFactory(vendorsAuthConfigFactory); + String vendorAuthConfigFactoryClass = vendorsAuthConfigFactory.getClass().getName(); + printIt("vendorACFClass = " + vendorAuthConfigFactoryClass); + + // First verify we were able to change our AuthConfigFactory to use vendors + if (vendorAuthConfigFactoryClass.equals(JASPICData.TSSV_ACF)) { + // ohoh - nothing was changed we still have CTS default AuthConfigFactory + String str = "FAILURE - Could not set vendors AuthConfigFactory."; + printIt(str); + throw new Exception("ACFSwitchFactorys : FAILED"); + } + + // Next - verify our calls to getFactory() are getting a non-null factory + AuthConfigFactory testAuthConfigFactory = AuthConfigFactory.getFactory(); + if (testAuthConfigFactory == null) { + String str = "FAILURE - Could not get vendors AuthConfigFactory."; + printIt(str); + throw new Exception("ACFSwitchFactorys : FAILED"); + } else { + String str = "Successfully set vendors ACF."; + printIt(str); + } + + String newlySetAuthConfigFactoryClass = testAuthConfigFactory.getClass().getName(); // should be + // confirming + // Impls ACF + printIt("newlySetACFClass = " + newlySetAuthConfigFactoryClass); + + // next - verify getFactory() returns the expected/correct vendor class + // name + if (!newlySetAuthConfigFactoryClass.equals(vendorAuthConfigFactoryClass)) { + String str = "FAILURE - calling getFactory(). "; + str += "did not return expected/vendor ACF classname of: " + vendorAuthConfigFactoryClass; + printIt(str); + throw new Exception("ACFSwitchFactorys : FAILED"); + } else { + String msg = "newlySetACFClass == vendorACFClass == " + vendorAuthConfigFactoryClass; + printIt(msg); + } + + printIt("ACFSwitchFactorys : PASSED"); + } catch (Exception ex) { + // unknown exception + String msg = "got unknown exception: " + ex.getMessage(); + printIt(msg); + ex.printStackTrace(); + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("ACFSwitchFactorys : passed"); + } + + /* + * this tests that the ACF we currently expect is set to our CTS default ACF. The only way this should be, is if the + * runtime correctly read the ACF from the security files property of: authconfigprovider.factory + */ + public void _testACFComesFromSecFile() throws Exception { + + // verify our call to getFactory() gets the default ACF instance + AuthConfigFactory testAuthConfigFactory = AuthConfigFactory.getFactory(); + if (testAuthConfigFactory == null) { + printIt("FAILURE - Could not get AuthConfigFactory."); + throw new Exception("testACFComesFromSecFile : FAILED"); + } + String defaultAuthConfigFactoryClass = testAuthConfigFactory.getClass().getName(); + printIt("testACFComesFromSecFile.defaultACFClass = " + defaultAuthConfigFactoryClass); + + // Verify AuthConfigFactory matches the AuthConfigFactory that *should* exist in security prop file + if (!defaultAuthConfigFactoryClass.equals(JASPICData.TSSV_ACF)) { + printIt("FAILURE - our default ACF does not match our default ACF"); + throw new Exception("testACFComesFromSecFile : FAILED"); + } + + printIt("testACFComesFromSecFile passed"); + } + + /* + * This is verifying that the invocation of removeRegistration() with an invalid and non-existant regId will return + * false. This assists with testing assertion JASPIC:SPEC:345 + */ + public void _ACFRemoveRegistrationWithBadId() { + + try { + Random rand = new Random(); + + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + + // create a unique registration Id + String uniqueRegId = "someMadeUpandNonExistantRegId"; + uniqueRegId += String.valueOf(new Integer(rand.nextInt()).toString()); + boolean bval = acf.removeRegistration(uniqueRegId); + if (bval == true) { + printIt("FAILURE - acf.removeRegistration(invalidId) returned true"); + } + } catch (Exception ex) { + String str = "FAILURE - acf.removeRegistration(invalidId) exception msg: "; + str += ex.getMessage(); + printIt(str); + ex.printStackTrace(); + } + + printIt("ACFRemoveRegistrationWithBadId passed"); + } + + /* + * private convenience method to check the description of the ACF's registrationContext. + */ + private boolean verifyDescriptionSetOK(AuthConfigFactory authConfigFactory, String registrationId, String desc) { + boolean bval = false; + try { + AuthConfigFactory.RegistrationContext authConfigFactoryReg; + authConfigFactoryReg = authConfigFactory.getRegistrationContext(registrationId); + if ((authConfigFactoryReg != null) && (desc.equals(authConfigFactoryReg.getDescription()))) { + bval = true; + } + } catch (Exception ex) { + } + + return bval; + } + + /* + * This is one of our more comprehensive tests and it touches on several aspects of registration. + * + * This is used to test that we can register a provider using the two different ACF registerProvider() calls. (one call + * does persistent registration such that the registration will persist between jvm recycles but the other is an + * in-memory registration that will not persist between jvm recycles. The passed in arg: usePersistRegistration + * determines which registration method we are testing. + * + * This test does the following: - get current (CTS default) ACF - persistently register the CTS ACP's within *vendors* + * ACF - register a CTS provider (based on passed in arg of usePersistRegistration) in vendors ACF - JASPIC:SPEC:341 or + * JASPIC:SPEC:342 - verify our ACP registered okay - re-register the same ACP - verify re-registering ACP did NOT add + * another regId AND that the newly register ACP re-used same regID AND that the description also got replaced - per + * assertions of: JASPIC:SPEC:340 and JASPIC:SPEC:343 - unregister our CTS provider - restore CTS default ACF + * + */ + public void _ACFRegisterOnlyOneACP(String logFileLocation, String providerConfigFilePath, String vendorAuthConfigFactoryClass, + boolean usePersistRegistration) throws Exception { + + // (persistently) register providers in vendor factory + printIt("CommonTests._ACFRegisterOnlyOneACP(): logFileLocation = " + logFileLocation); + printIt("CommonTests._ACFRegisterOnlyOneACP(): providerConfigFilePath = " + providerConfigFilePath); + printIt("CommonTests._ACFRegisterOnlyOneACP(): vendorACFClass = " + vendorAuthConfigFactoryClass); + + AuthConfigFactory vendorAuthConfigFactory = CommonUtils.register(logFileLocation, providerConfigFilePath, + vendorAuthConfigFactoryClass); + try { + if (vendorAuthConfigFactory == null) { + throw new Exception("Failed trying to register ACPs with vendors AuthConfigFactory"); + } + + // This is to ensure we do NOT have a previously registered standalone + // provider + // which could be due to a previously incomplete or faulty run. Also due + // to RI bug + // we have to add the provider, then remove it to ensure its not there. + // String clearId = CommonUtils.getRegisteredProviderID(AuthConfigProviderLayer, + // AuthConfigProviderAppContext, null); + String clearId = vendorAuthConfigFactory.registerConfigProvider(AuthConfigProviderClass, null, AuthConfigProviderLayer, + AuthConfigProviderAppContext, AuthConfigProviderDesc); + vendorAuthConfigFactory.removeRegistration(clearId); + + // get the list of vendor registered provider ID's + String[] origRegIDs = vendorAuthConfigFactory.getRegistrationIDs(null); + int defaultLength = origRegIDs.length; + + // Dump registered provider info - befor we try to add another ACP + printIt("Dumping providers which should only contain those in ProviderConfiguration.xml"); + CommonUtils.dumpProviders(vendorAuthConfigFactory, origRegIDs); + + // try to register a standalone provider for the 1st time + String regID1 = null; + if (usePersistRegistration) { + // use persistent reigstration - JASPIC:SPEC:341 + regID1 = vendorAuthConfigFactory.registerConfigProvider(AuthConfigProviderClass, null, AuthConfigProviderLayer, + AuthConfigProviderAppContext, AuthConfigProviderDesc); + } else { + // use in-memory (ie non-persistent) registration - JASPIC:SPEC:342 + regID1 = vendorAuthConfigFactory.registerConfigProvider(null, AuthConfigProviderLayer, AuthConfigProviderAppContext, + AuthConfigProviderDesc); + } + + String[] firstRegIDs = vendorAuthConfigFactory.getRegistrationIDs(null); + // now dump registered provider info - after adding 1 more ACP + printIt("Dumping providers which should show a newly added standalone provider."); + CommonUtils.dumpProviders(vendorAuthConfigFactory, firstRegIDs); + + // verify that our 1st attempt at adding the an ACP did register ok + if ((regID1 == null) || (firstRegIDs.length <= defaultLength)) { + printIt("regID1 = " + regID1); + printIt("firstRegIDs.length = " + firstRegIDs.length + " defaultLength = " + defaultLength); + String err = "Could not register provider in vendors ACF class."; + err += " Problematic provider = " + AuthConfigProviderClass; + throw new Exception(err); + } else { + printIt("a new entry was registered"); + } + + // try to re-register the same standalone provider + // note: we have different description - which is okay + // this tests JASPIC:SPEC:340 / JASPIC:SPEC:343 + String changedDesc = "some other description"; + String regID2 = null; + if (usePersistRegistration) { + // use persistent registration + regID2 = vendorAuthConfigFactory.registerConfigProvider(AuthConfigProviderClass, null, AuthConfigProviderLayer, + AuthConfigProviderAppContext, changedDesc); + } else { + // use in-memory (ie non-persistent) registration + regID2 = vendorAuthConfigFactory.registerConfigProvider(null, AuthConfigProviderLayer, AuthConfigProviderAppContext, + changedDesc); + } + + String[] secondRegIDs = vendorAuthConfigFactory.getRegistrationIDs(null); + // now dump registered provider info - after re-adding the same ACP + String msg = "Dumping providers which should show the standalone "; + msg += "provider with slighly different description."; + printIt(msg); + CommonUtils.dumpProviders(vendorAuthConfigFactory, secondRegIDs); + + // verify that our 2nd attempt at adding the same ACP correctly replaced + // the + // same provider (based on appcontext and msg layer) and did NOT add + // another entry + if ((regID2 == null) || (firstRegIDs.length != secondRegIDs.length)) { + String err = "Failure during re-registering of ACP."; + err += " Attempts to re-register should replace the provider not add more copies of it."; + err += " Problematic provider = " + AuthConfigProviderClass; + throw new Exception(err); + } else { + printIt("the re-registered provider was replaced."); + } + + // per assertion JASPIC:SPEC:340/JASPIC:SPEC:343 - regID1 and regID2 MUST + // be the same after a re-registering + // of same ACP based on javadoc reference for registerConfigProvider + if (!regID1.equals(regID2)) { + printIt("regID1 = " + regID1 + " regID2 = " + regID2); + String err = "Failure during re-registering of ACP - regID was invalid."; + err += " During a re-registration, the registration ID must be the same"; + throw new Exception(err); + } + + // verify our re-registered provider did replace the ACP and the + // Description + // (per assertion JASPIC:SPEC:340 / JASPIC:SPEC:343) + if (!verifyDescriptionSetOK(vendorAuthConfigFactory, regID2, changedDesc)) { + String err = "Failure during verification of ACP re-registration related "; + err += "to description overwrite"; + throw new Exception(err); + } + + // unregister our standalone provider - + vendorAuthConfigFactory.removeRegistration(regID2); + + String[] finalRegIDs = vendorAuthConfigFactory.getRegistrationIDs(null); + // now dump registered provider info - after re-adding the same ACP + printIt("Dumping providers which should show only providers from ProviderConfiguration.xml."); + CommonUtils.dumpProviders(vendorAuthConfigFactory, finalRegIDs); + + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("AuthConfigFactory ACFRegisterOnlyOneACP passed"); + } + + /* + * this tests the ability to unregister a provider. First, it must register a provider, then once registered, the + * ability to unregister is then tested. + */ + public void _ACFUnregisterACP(String logFileLocation, String providerConfigFilePath, String vendorACFClass) throws Exception { + try { + // register providers in vendor factory + AuthConfigFactory vendorACF = CommonUtils.register(logFileLocation, providerConfigFilePath, vendorACFClass); + if (vendorACF == null) { + throw new Exception("Failed trying to register ACPs with vendors ACF"); + } + + // get list of vendors registered provider ID's + String[] origRegIDs = vendorACF.getRegistrationIDs(null); + + // dump registered provider info - befor we try to register a provider + printIt("Dumping providers from ProviderConfiguration.xml."); + CommonUtils.dumpProviders(vendorACF, origRegIDs); + + // try to register a standalone provider for the 1st time + String regID1 = vendorACF.registerConfigProvider(null, AuthConfigProviderLayer, AuthConfigProviderAppContext, + AuthConfigProviderDesc); + + String[] firstRegIDs = vendorACF.getRegistrationIDs(null); + // now dump registered provider info - after registering a provider + printIt("Dumping providers to show that a provider was added."); + CommonUtils.dumpProviders(vendorACF, firstRegIDs); + + // verify that our 1st attempt at adding the an ACP did register ok + if ((regID1 == null) || (firstRegIDs.length <= origRegIDs.length)) { + printIt("regID1 = " + regID1); + printIt("firstRegIDs.length = " + firstRegIDs.length + " origRegIDs.length = " + origRegIDs.length); + String err = "Error registering provider in vendors ACF class."; + err += " Problematic provider = " + AuthConfigProviderClass; + throw new Exception(err); + } else { + printIt("a new entry was registered"); + } + + // now unregister our standalone provider + boolean wasFound = vendorACF.removeRegistration(regID1); + + String[] finalRegIDs = vendorACF.getRegistrationIDs(null); + // now dump provider info - after unregistering provider + printIt("Dumping providers to only show providers in ProviderConfiguration.xml."); + CommonUtils.dumpProviders(vendorACF, finalRegIDs); + + // verify removeRegistration returned proper boolean + if (!wasFound) { + String err = "ERROR: removeRegistration could not find the regID to remove."; + throw new Exception(err); + } else { + printIt("removeRegistration found the regID to remove"); + } + + // verify registry has proper number of items in it + if (finalRegIDs.length >= firstRegIDs.length) { + String err = "ERROR: removeRegistration had issue removing registry entry."; + throw new Exception(err); + } + + // verify we can't access our regId anymore since it should be + // unregistered + if (true == vendorACF.removeRegistration(regID1)) { + String err = "ERROR: removeRegistration still thinks regID is valid but it should not be"; + throw new Exception(err); + } + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("AuthConfigFactory ACFRegisterOnlyOneACP passed"); + } + + /* + * basic registration test to verify we can register the CTS providers within the vendors ACF. (registration is + * persistent rgistration) + */ + public void _AuthConfigFactoryRegistration(String logFileLocation, String providerConfigFilePath, String vendorACFClass) + throws Exception { + + try { + // get default ACF being used + AuthConfigFactory defaultACF = AuthConfigFactory.getFactory(); + String defaultACFClass = defaultACF.getClass().getName(); + printIt("ACFUnregisterACP.defaultACFClass = " + defaultACFClass); + + // register providers in vendor factory + AuthConfigFactory vendorACF = CommonUtils.register(logFileLocation, providerConfigFilePath, vendorACFClass); + + if (vendorACF == null) { + throw new Exception("Failed trying to register ACPs with vendors ACF"); + } + + printIt("vendorACFClass = " + vendorACFClass); + + // first verify we were able to change our ACF to use vendors + if (vendorACFClass.equals(JASPICData.TSSV_ACF)) { + // ohoh - nothing was changed we still have CTS default ACF + String str = "FAILURE - Could not set vendors AuthConfigFactory."; + printIt(str); + throw new Exception("AuthConfigFactoryRegistration : FAILED"); + } + + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("AuthConfigFactoryRegistration passed"); + } + + /* + * This will test the ability to get the config provider when there are combos of null layers or app contexts. This + * should return the provider for the specified app context and null layer. This will also verify the ability to + * register provider a provider that has non-null appcontext and null layer with a listener. Then a verification that + * the notification works okay is done. + * + * Precedence rules are as follows: + * + * 1 - The provider specifically registered for the values passed as the layer and + * appContext arguments shall be selected. + * + * 2 - If no provider is selected according to the preceding rule, the provider + * specifically registered for the value passed as the appContext argument and for all (that is, null) layers shall be + * selected. + * + * 3 - If no provider is selected according to the preceding rules, the provider specifically registered for + * the value passed as the layer argument and for all (that is, null) appContexts shall be selected. + * + * 4 - If no provider + * is selected according to the preceding rules, the provider registered for all (that is, null) layers and for all + * (that is, null) appContexts shall be selected. + * + * 5 - If no provider is selected according to the preceding rules, the + * factory shall terminate its search for a registered provider. + * + */ + public void _ACFTestPrecedenceRules(String vendorACFClass, boolean isPersistent) throws Exception { + + String layers[] = { null, "phakeMsgLayer1" }; + String contexts[] = { null, "ctxt1" }; + AuthConfigFactory authConfigFactory = null; + String regId = null; + String[] regIds = new String[layers.length * contexts.length]; + + try { + if (vendorACFClass != null) { + authConfigFactory = CommonUtils.getAuthConfigFactory(vendorACFClass); + } else { + // error - we need valid vendor acf + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + /* + * loop thru and create/register a new acp for each of the msglayer/appcontext combinations. Verify that the call to + * getConfigProvider() returns appropriate vals - even for case of null msg layer or null appcontext. + */ + String description = "acp1"; + int count = 0; + for (String layer : layers) { + for (String context : contexts) { + if (isPersistent) { + // Do persistent registration + regId = authConfigFactory.registerConfigProvider( + AuthConfigProviderClass, + null, + layer, + context, + description); + } else { + // Do in-memory registration + regId = authConfigFactory.registerConfigProvider( + new TSAuthConfigProviderStandalone((Map) null, null, null, description), + layer, + context, + description); + } + + printIt("regId=" + regId + " for layer=" + layer + " " + " ctxt=" + context); + + // Test precedence rule 1 + boolean okay = testPrecedenceRule1(authConfigFactory, regId, layer, context, description); + if (!okay) { + printIt("Failed getConfiProvider() precedence rule 1."); + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + regIds[count] = regId; + count++; + } + } + + // test precedence rule 2 + // This is when only specifying appContext and null layer + // which should return all matching appContexts + boolean okay = testPrecedenceRule2(authConfigFactory, isPersistent); + if (!okay) { + printIt("Failed getConfiProvider() precedence rule 2."); + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + // test precedence rule 3 + // This is when only specifying msgLayer and null appContext + // which should return the provider registered with that msgLayer + okay = testPrecedenceRule3(authConfigFactory, isPersistent); + if (!okay) { + printIt("Failed getConfiProvider() precedence rule 3."); + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + // test precedence rule 4 + // This is when we are trying to retrieve a registered acp + // that does not have matching context or layer - in which case + // acp registered with null layer and null context should be + // returned - if there was an acp registered with null/null) + okay = testPrecedenceRule4(authConfigFactory, isPersistent); + if (!okay) { + printIt("Failed getConfiProvider() precedence rule 4."); + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + // cleanup ACPs that we registered in this method + okay = removeRegisteredACPs(authConfigFactory, regIds); + if (!okay) { + // we expect to get warning about unregistering ACPs here + printIt("Okay to ignore previous WARNING from call to removeRegisteredACPs"); + } + + // test precedence rule 5 + // This is when we are trying to call getCOnfigProvider() + // with non-registered layer/context values. + // This verifies that no providers could be found and + // so factory must terminate its search for registerd acp. + okay = testPrecedenceRule5(authConfigFactory, isPersistent); + if (!okay) { + printIt("Failed getConfiProvider() precedence rule 5."); + throw new Exception("ACFTestPrecedenceRules : FAILED"); + } + + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + + printIt("ACFTestPrecedenceRules passed"); + } + + /* + * test is verifying we can get the correct provider back with the specifying of a non-existing msg layuer and context + */ + private boolean testPrecedenceRule5(AuthConfigFactory acf, boolean isPersistent) throws Exception { + boolean rval = true; + String MSG_LAYER = "phakeMsgLayer5"; // non-existant msg layer + String APP_CTXT = "phakeContext5"; // non-existant context + + AuthConfigProvider gottenAcp = acf.getConfigProvider(MSG_LAYER, APP_CTXT, null); + if (gottenAcp != null) { + printIt("Error - precedence rule 5 failure"); + String str = ((TSAuthConfigProviderStandalone) gottenAcp).getDescription(); + printIt("ERROR - precedence rule 5 returned acp desc = " + str); + rval = false; + } + + return rval; + } + + /* + * This will register two ACP's using same class but differ layer and contexts. One acp with have null/null and the + * other acp will have non-null layer/context. This then verifies it gets the correct ACP back (based on description). + */ + private boolean testPrecedenceRule4(AuthConfigFactory acf, boolean isPersistent) throws Exception { + boolean rval = true; + TSAuthConfigProviderStandalone acp4, acp4b; + acp4 = new TSAuthConfigProviderStandalone((Map) null, null, null, "acp4"); + acp4b = new TSAuthConfigProviderStandalone((Map) null, null, null, "acp4b"); + String layer = null; + String context = null; + String layer2 = "layer4"; + String context2 = "context 4"; + String rId1 = null, rId2 = null; + Map props; + String strDesc1 = "acp4"; + String strDesc2 = "acp4b"; + + if (isPersistent) { + // do persistent registration + props = setDescriptionProp(strDesc1); + rId1 = acf.registerConfigProvider(AuthConfigProviderClass, props, layer, context, strDesc1); + props = setDescriptionProp(strDesc2); + rId2 = acf.registerConfigProvider(AuthConfigProviderClass, props, layer2, context2, strDesc2); + } else { + // do in-memory registration + rId1 = acf.registerConfigProvider(acp4, layer, context, strDesc1); + rId2 = acf.registerConfigProvider(acp4b, layer2, context2, strDesc2); + } + + AuthConfigProvider gottenAcp = acf.getConfigProvider(layer, context, null); + if (gottenAcp == null) { + printIt("Error - precedence rule 4 failure"); + return false; + } + + // make sure we get back provider registerd with null/null + AuthConfigFactory.RegistrationContext rc = acf.getRegistrationContext(rId1); + String str = rc.getDescription(); + String str2 = ((TSAuthConfigProviderStandalone) gottenAcp).getDescription(); + boolean bMatch = (str != null) && (str.equals(strDesc1)) && (str2 != null) && (str2.equals(strDesc1)); + if (!bMatch) { + String err = "ERROR - precedence rule 4 returned wrong acp"; + err += " rc.getDescription() = " + str + "gottenAcp.desc=" + str2; + printIt(err); + rval = false; + } + + // also - test calling with invalid layer/context gets the provider + // that was registered with null layer and null context + // This should return provider registered with null/null + AuthConfigProvider gottenAcp2 = acf.getConfigProvider("nonExistantLayer", "nonExistantContext", null); + if (gottenAcp2 == null) { + printIt("Error - precedence rule 4b failure"); + rval = false; + } + + // since gottenAcp2 should have returned the provider that was + // registered with null/null which should have desc = strDesc1 + if (gottenAcp2 != null) { + str = ((TSAuthConfigProviderStandalone) gottenAcp2).getDescription(); + } else { + String err = "ERROR - unexpected null ACP encountered. (gottenAcp2 == null)"; + printIt(err); + rval = false; + } + + // call to getRegContext should return desc that corresponds + // to the acp we registered with rId2 + rc = acf.getRegistrationContext(rId2); + str2 = rc.getDescription(); // should match strDesc2 + + bMatch = (str2 != null) && (str2.equals(strDesc2)) && (str != null) && (str.equals(strDesc1)); + if (!bMatch) { + String err = "ERROR - precedence rule 4 (2nd check) returned wrong acp"; + err += " rc.getDescription() = " + str2 + "gottenAcp2.description=" + str; + printIt(err); + rval = false; + } + + // do some cleanup + acf.removeRegistration(rId1); + acf.removeRegistration(rId2); + + return rval; + } + + /* + * test is verifying we can get the correct provider back with the specifying of the unique msg layer and null/no + * appContext. + */ + private boolean testPrecedenceRule3(AuthConfigFactory acf, boolean isPersistent) throws Exception { + boolean rval = true; + TSAuthConfigProviderStandalone acp3, acp3a; + String MSG_LAYER = "phakeMsgLayer1"; // unique msg layer + String APP_CTXT = "context3"; + String rId = null; + String rId2 = null; + Map props; + String strDesc1 = "acp3"; + String strDesc2 = "acp3a"; + + acp3 = new TSAuthConfigProviderStandalone((Map) null, null, null, strDesc1); + acp3a = new TSAuthConfigProviderStandalone((Map) null, null, null, strDesc2); + + if (isPersistent) { + // do persistent registration + props = setDescriptionProp(strDesc1); + rId = acf.registerConfigProvider(AuthConfigProviderClass, props, MSG_LAYER, null, strDesc1); + props = setDescriptionProp(strDesc2); + rId2 = acf.registerConfigProvider(AuthConfigProviderClass, props, MSG_LAYER, APP_CTXT, strDesc2); + } else { + // do in-memory registration + rId = acf.registerConfigProvider(acp3, MSG_LAYER, null, strDesc1); + rId2 = acf.registerConfigProvider(acp3a, MSG_LAYER, APP_CTXT, strDesc2); + } + + // we should get back acp registered with matching layer and null appcontext + AuthConfigProvider gottenAcp = acf.getConfigProvider(MSG_LAYER, null, null); + if (gottenAcp == null) { + printIt("Error - precedence rule 3 failure"); + return false; + } + + // make sure we didnt accidentally get back wrong provider + // we should get back acp with description = strDesc1 + AuthConfigFactory.RegistrationContext rc = acf.getRegistrationContext(rId); + String str = rc.getDescription(); + String str2 = ((TSAuthConfigProviderStandalone) gottenAcp).getDescription(); + boolean bMatch = (str != null) && (str.equals(strDesc1)) && (str2 != null) && (str2.equals(strDesc1)); + if (!bMatch) { + printIt("ERROR - precedence rule 3 returned wrong acp"); + rval = false; + } + + // do some cleanup + acf.removeRegistration(rId); + acf.removeRegistration(rId2); + + return rval; + } + + /* + * This verifies we can get the correct provider back with the specifying of the unique appContext and null/no msglayer. + */ + private boolean testPrecedenceRule2(AuthConfigFactory acf, boolean isPersistent) throws Exception { + boolean rval = true; + TSAuthConfigProviderStandalone acp2, acp2a; + String rId = null; + String rId2 = null; + Map props; + String strDesc1 = "acp2"; + String strDesc2 = "acp2a"; + + acp2 = new TSAuthConfigProviderStandalone((Map) null, null, null, strDesc1); + acp2a = new TSAuthConfigProviderStandalone((Map) null, null, null, strDesc2); + + if (isPersistent) { + // do persistent registration + props = setDescriptionProp(strDesc1); + rId = acf.registerConfigProvider(AuthConfigProviderClass, props, null, "ctxt2", strDesc1); + props = setDescriptionProp(strDesc2); + rId2 = acf.registerConfigProvider(AuthConfigProviderClass, props, "nonexistLayer", "ctxt2", strDesc2); + } else { + // do in-memory registration + rId = acf.registerConfigProvider(acp2, null, "ctxt2", strDesc1); + rId2 = acf.registerConfigProvider(acp2a, "nonexistLayer", "ctxt2", strDesc2); + } + + AuthConfigProvider gottenAcp = acf.getConfigProvider(null, "ctxt2", null); + if (gottenAcp == null) { + printIt("Error - precedence rule 2 failure"); + return false; + } + + // make sure we didnt accidentally get back wrong provider (where strDesc1 + // is description for correct acp and anything else is wrong acp + AuthConfigFactory.RegistrationContext rc = acf.getRegistrationContext(rId); + String str = rc.getDescription(); + String str2 = ((TSAuthConfigProviderStandalone) gottenAcp).getDescription(); + boolean bMatch = (str != null) && (str.equals(strDesc1)) && (str2 != null) && (str2.equals(strDesc1)); + if (!bMatch) { + String err = "ERROR - precedence rule 2 returned wrong acp "; + err += " rc.getDescription()= " + str; + err += " gottenAcp.getDescription()=" + str2; + printIt(err); + rval = false; + } + + // do some cleanup + acf.removeRegistration(rId); + acf.removeRegistration(rId2); + + return rval; + } + + private boolean testPrecedenceRule1(AuthConfigFactory acf, String regId, String layer, String context, String desc) throws Exception { + boolean rval = true; + + AuthConfigProvider gottenAcp; + gottenAcp = acf.getConfigProvider(layer, context, null); + if (gottenAcp == null) { + printIt("Error - no ACP found in call to getConfigProvider() with layer=" + layer + "and context=" + context); + rval = false; + } else { + AuthConfigFactory.RegistrationContext rc; + rc = acf.getRegistrationContext(regId); + String str = rc.getDescription(); + boolean bMatch = (str != null) && (str.equals(desc)); + if (!bMatch) { + printIt("ERROR getConfigProvider() returned wrong acp=" + str); + rval = false; + } + + // next, confirm the getConfigProvider() above returned a valid + // regId that matches current layer & context info + boolean ok = verifyGetConfigProvider(acf, gottenAcp, regId, layer, context); + if (!ok) { + printIt("ERROR no registration found for regIds=" + regId); + rval = false; + } + } + + return rval; + } + + /* + * This is a convenience method that sets a property which can be used by the TSAuthConfigProviderStandalone class which + * looks for this property during instantiation by a persistent registration. Said differently, we use this property to + * set the description when we are creating a new/persistent ACP registration. + */ + private Map setDescriptionProp(String val) { + HashMap props = new HashMap<>(); + props.put(TSAuthConfigProviderStandalone.DESC_KEY, val); + return props; + } + + /* + * This is used to verify the RegistrationContext value for isPersistent() matches what we expect to be set. + * + */ + public boolean verifyRegContextPersitentVal(AuthConfigFactory acf, String regId, boolean expectedPersistent) throws Exception { + boolean rval = false; + + AuthConfigFactory.RegistrationContext rc; + rc = acf.getRegistrationContext(regId); + + if (rc == null) { + // error - cant find context for regId but should be able to + printIt("ERROR could not get RegistrationContext in verifyRegContextPersitentVal"); + return false; + } + + if (rc.isPersistent() == expectedPersistent) { + rval = true; + } + + return rval; + } + + /* + * This is used to verify that the passed in regId contains the expected msg layer and appcontext values. It also + * verifies that the passed in regId corresponds with the passed in acp. + */ + public boolean verifyGetConfigProvider(AuthConfigFactory acf, AuthConfigProvider acp, String regId, String expectedLayer, + String expectedContext) throws Exception { + boolean rval = true; + boolean bLayer = false; + boolean bContext = false; + + AuthConfigFactory.RegistrationContext rc; + rc = acf.getRegistrationContext(regId); + + if (rc == null) { + // error - cant find context for regId but should be able to + printIt("ERROR could not get RegistrationContext in verifyGetConfigProvider"); + return false; + } + + if (((expectedLayer != null) && expectedLayer.equals(rc.getMessageLayer())) + || ((expectedLayer == null) && (rc.getMessageLayer() == null))) { + bLayer = true; + } + + if (((expectedContext != null) && expectedContext.equals(rc.getAppContext())) + || ((expectedContext == null) && (rc.getAppContext() == null))) { + bContext = true; + } + + // make sure our regId is registered for our acp + String[] regIds = acf.getRegistrationIDs(acp); + boolean regIdMatch = false; + for (int ii = 0; ii < regIds.length; ii++) { + if ((regId != null) && (regId.equals(regIds[ii]))) { + regIdMatch = true; + } + } + + if (bLayer && bContext && regIdMatch) { + rval = true; + } + + return rval; + } + + /* + * This will test the ability to notify listeners for acps that are registered with a combo of profile layers and app + * contexts. This assists with testing: JASPIC:SPEC:336, JASPIC:SPEC:338, JASPIC:SPEC:339, JASPIC:SPEC:344, + * JASPIC:SPEC:337 + */ + public void _ACFTestNotifyOnUnReg(String vendorACFClass, boolean isPersistent) throws Exception { + + String profileLayer[] = { null, "HttpServlet", "phakeProfile" }; + String context[] = { null, "ctxt2", "ctxt3" }; + AuthConfigFactory acf = null; + + try { + if (vendorACFClass != null) { + acf = CommonUtils.getAuthConfigFactory(vendorACFClass); + } else { + // error - we need valid vendor acf + throw new Exception("ACFTestNotifyOnUnReg : FAILED"); + } + + // create/register new providers for each layer-context combination + String[] regIds = createAndRegisterACPs(acf, isPersistent, profileLayer, context); + + // add listeners for each layer-context combo to ACPs - JASPIC:SPEC:337 + TSRegistrationListener tlistener[] = addListenersToACP(acf, profileLayer, context); + + // at this point, our listeners likely have not have been called, + // but to be sure - lets reset our listeners 'notified' flag + for (int ii = 0; ii < tlistener.length; ii++) { + tlistener[ii].resetNotifyFlag(); + } + + // assert JASPIC:SPEC:344 + // remove registered ACPs using the passed in list of regIds. This + // must generate notifications to listeners associated with those ACPs + boolean ok = removeRegisteredACPs(acf, regIds); + if (!ok) { + // one of registered ids could not be found and/or unregistered + throw new Exception("ACFTestNotifyOnUnReg : FAILED"); + } + + // verify notifications occurred during unregistration JASPIC:SPEC:338 + ok = verifyNotifications(acf, tlistener, profileLayer, context); + if (!ok) { + throw new Exception("ACFTestNotifyOnUnReg : FAILED"); + } + + // do some cleanup and verify if it went okay + ok = detachAllListeners(acf, tlistener, profileLayer, context); + if (!ok) { + // got error during detach + throw new Exception("ACFTestNotifyOnUnReg : FAILED"); + } + + } catch (Exception ex) { + throw ex; + } finally { + // restore original (CTS) factory class + CommonUtils.resetDefaultACF(); + } + printIt("ACFTestNotifyOnUnReg passed"); + } + + /* + * This verifies notifications for the combinations of passed in layer and contexts. returns false if an error is + * encountered. + */ + public boolean verifyNotifications(AuthConfigFactory acf, TSRegistrationListener[] tlistener, String[] layer, String[] context) + throws Exception { + boolean bval = true; + + // verify notifications occurred during unregistration + for (int kk = 0; kk < tlistener.length; kk++) { + for (int ii = 0; ii < layer.length; ii++) { + for (int jj = 0; jj < context.length; jj++) { + boolean bPassed = false; + bPassed = tlistener[kk].check(layer[ii], context[jj]); + String str = "layer=" + layer[ii] + " and context=" + context[jj]; + if (!bPassed) { + printIt(" Failed listener for: " + str); + bval = false; + } + } + } + } + + return bval; + } + + /* + * This detaches all listeners and also checks that the call to detachListenre never returns null. used in testing + * assert JASPIC:SPEC:339 returns false if an error is encountered. + */ + public boolean detachAllListeners(AuthConfigFactory acf, TSRegistrationListener[] tlistener, String[] layer, String[] context) + throws Exception { + boolean bval = true; + + for (int kk = 0; kk < tlistener.length; kk++) { + for (int ii = 0; ii < layer.length; ii++) { + for (int jj = 0; jj < context.length; jj++) { + // now, detach listeners + String rval[] = acf.detachListener(tlistener[kk], layer[ii], context[jj]); + if (rval == null) { + // should NEVER be null (per api doc) + String str = "layer=" + layer[ii] + " and context=" + context[jj]; + printIt("Failed detachListener for: " + str); + bval = false; + } + } + } + } + + return bval; + } + + /* + * This takes the passed in list or ACP regIds and unregisteres those ACp's from the passed in ACF. This returns false + * if a registration is deemed invalid and cant be removed. + */ + public boolean removeRegisteredACPs(AuthConfigFactory acf, String[] regIds) throws Exception { + + boolean bval = true; + + // remove registrations with listeners - should call notify on listeners + for (int ii = 0; ii < regIds.length; ii++) { + printIt(" REMOVING PROVIDERS TO INVOKE NOTIFY() for regId=" + regIds[ii]); + boolean result = false; + + result = acf.removeRegistration(regIds[ii]); + if (!result) { + printIt("WARNING: no registration found for regIds=" + regIds[ii]); + bval = false; + } + } + + return bval; + } + + /* + * This method will create new listeners for the passed in layer/context combinations. After creating each listener, it + * is added to the ACP that matches the context/layer for that new listener. This returns an array or newly created + * listeners that were added to ACP's. + */ + public TSRegistrationListener[] addListenersToACP(AuthConfigFactory acf, String[] layer, String[] context) throws Exception { + + int theSize = layer.length * context.length; // num listeners + TSRegistrationListener[] tlistener = new TSRegistrationListener[theSize]; + AuthConfigProvider tmpAcp[] = new AuthConfigProvider[theSize]; + + // Create listeners, then add them to ACPs + int count = 0; + for (int ii = 0; ii < layer.length; ii++) { + for (int jj = 0; jj < context.length; jj++) { + tlistener[count] = new TSRegistrationListener(layer[ii], context[jj]); + tmpAcp[ii] = acf.getConfigProvider(layer[ii], context[jj], tlistener[count]); + if (tmpAcp[ii] == null) { + printIt(" no ACP found in call to getConfigProvider() with layer=" + layer[ii] + "and context=" + context[jj]); + } + count++; + } + } + + return tlistener; + } + + /* + * This method creates and register a new ACP for each combination of the passed in layer and contexts. So if there are + * 3 layers and 3 contexts passed into this method, there will be 3 * 3 = 9 different ACPs registered in the passed in + * ACF. use the passed in "isPersistent" flag to specify if we want our registeration to be persistent or in-memory only + * type of registration. + */ + public String[] createAndRegisterACPs(AuthConfigFactory acf, boolean isPersistent, String[] layer, String[] context) throws Exception { + + TSAuthConfigProviderStandalone acp; + String[] regIds = new String[layer.length * context.length]; + + try { + // register new acp's + int count = 0; + for (int ii = 0; ii < layer.length; ii++) { + for (int jj = 0; jj < context.length; jj++) { + String desc = AuthConfigProviderDesc + " layer=" + ii + " " + " ctxt=" + jj; + if (isPersistent) { + // do persistent registration + regIds[count] = acf.registerConfigProvider(AuthConfigProviderClass, null, layer[ii], context[jj], desc); + } else { + // do in-memory registration + acp = new TSAuthConfigProviderStandalone((Map) null, null); + regIds[count] = acf.registerConfigProvider(acp, layer[ii], context[jj], desc); + } + + // JASPIC:SPEC:336 + boolean bb = verifyRegContextPersitentVal(acf, regIds[count], isPersistent); + if (!bb) { + // failure - our reg context did not have setting that matches the + // type of registration we used + String ss = new String("ERROR - RegistrationContext.isPersistent() mismatch"); + printIt(ss); + throw new Exception(ss); + } + + String str = "regId=" + regIds[count] + " for layer=" + ii + " " + " ctxt=" + jj; + printIt(str); + count++; + } + } + + } catch (Exception ex) { + printIt("Got Exception in createAndRegisterACPs()"); + throw ex; + } + + return regIds; + } + + public void printIt(String str) { + if (out == null) { + System.out.println(str); + } else { + // lets print them both so we get info in server.log and client side log + out.println(str); + System.out.println(str); + } + } + + // -------------------------------------------------------------------- + // Nested Class: MyRegistrationListener + // + public class MyRegistrationListener implements RegistrationListener { + String theProfileLayer = null; + + String appContext = null; + + boolean notified = false; + + public MyRegistrationListener() { + } + + public MyRegistrationListener(String theProfileLayer, String appContext) { + this.theProfileLayer = theProfileLayer; + this.appContext = appContext; + } + + public String getProfileLayer() { + return this.theProfileLayer; + } + + public void setProfileLayer(String val) { + this.theProfileLayer = val; + } + + public String getAppContext() { + return this.appContext; + } + + public void setAppContext(String val) { + this.appContext = val; + } + + public void resetNotifyFlag() { + notified = false; + } + + public boolean notified() { + return notified; + } + + public boolean check(String layer, String context) { + return true; + } + + @Override + public void notify(String layer, String context) { + boolean bLayersMatch = (theProfileLayer == layer) || theProfileLayer.equals(layer); + boolean bContextsMatch = (appContext == context) || appContext.equals(context); + + if (bLayersMatch && bContextsMatch) { + // successful notification + } else { + // error - notify had problem + } + } + } // end Nested Class MyRegistrationListener + +} // end Outer Class CommonTests diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonUtils.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonUtils.java new file mode 100644 index 0000000..cc3e8cf --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/CommonUtils.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * + * @author Oracle + */ +public class CommonUtils implements Serializable { + + private static final long serialVersionUID = 1L; + + public CommonUtils() { + + } + + public static AuthConfigFactory register(String logFileLocation, String providerConfigurationFileLocation, String vendorAuthConfigFactoryClass) { + + AuthConfigFactory acf = null; + try { + + printVerticalIndent(); + + // Get an instance of Vendor's AuthConfigFactory + AuthConfigFactory vFactory = getAuthConfigFactory(vendorAuthConfigFactoryClass); + + // Set vendor's AuthConfigFactory + AuthConfigFactory.setFactory(vFactory); + + // Get system default AuthConfigFactory + acf = AuthConfigFactory.getFactory(); + + if (acf != null) { + printIt("Default AuthConfigFactory class name = " + acf.getClass().getName()); + } else { + printError("Default AuthConfigFactory is null" + " can't register TestSuite Providers with null"); + return null; + } + + /** + * Read the ProviderConfiguration XML file This file contains the list of providers that needs to be loaded by the + * vendor's default AuthConfigFactory + */ + Collection providerConfigurationEntriesCollection = readProviderConfigurationXMLFile( + providerConfigurationFileLocation); + + ProviderConfigurationEntry pce = null; + + printVerticalIndent(); + Iterator iterator = providerConfigurationEntriesCollection.iterator(); + while (iterator.hasNext()) { + // obtain each ProviderConfigurationEntry and register it + // with vendor's default AuthConfigFactory + pce = iterator.next(); + + if (pce != null) { + printIt("pce.getProviderClassName() = " + pce.getProviderClassName()); + printIt("pce.getMessageLayer() = " + pce.getMessageLayer()); + printIt("pce.getApplicationContextId() = " + pce.getApplicationContextId()); + printIt("pce.getRegistrationDescription() = " + pce.getRegistrationDescription()); + if (pce.getProperties() != null) { + printIt("pce.getProperties.toString() = " + pce.getProperties().toString()); + } else { + printIt("pce.getProperties() = null"); + } + + printIt("Registering Provider " + pce.getProviderClassName() + " ..."); + Map newProps = getCleanACPProps(pce.getProperties()); + acf.registerConfigProvider(pce.getProviderClassName(), newProps, pce.getMessageLayer(), pce.getApplicationContextId(), + pce.getRegistrationDescription()); + printIt("Registration Successful"); + } else { + printIt("WARNING: pce was null and probably should not have been."); + } + } + printVerticalIndent(); + + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory"; + msg = msg + "or ACF.setFactory(). Check your server side security policies."; + printIt(msg); + ex.printStackTrace(); + + } catch (Exception e) { + printIt("Error Registering TestSuite AuthConfig Providers"); + e.printStackTrace(); + } + + return acf; + } + + private static Map getCleanACPProps(Map origProps) { + if (origProps == null) { + return null; + } + + HashMap props = new HashMap<>(); + + // loop thru passed in props and remove anything that is + // not of type String since only Strings are allowed in our + // calls to registerConfigProvider() + for (String key : origProps.keySet()) { + if (key != null) { + Object val = origProps.get(key); + if ((val != null) && ((val instanceof java.lang.String))) { + // we found entry that is not String so remove it + props.put(key, (String) val); + printIt("added key=" + key + " with value = " + val); + } else { + printIt("found non-string value for key=" + key); + } + } + } + + return props; + } + + /** + * This method instantiates and ruturns a AuthConfigFactory based on the specified className + */ + public static AuthConfigFactory getAuthConfigFactory(String className) { + AuthConfigFactory vFactory = null; + + if (className != null) { + try { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class clazz = Class.forName(className, true, loader); + vFactory = (AuthConfigFactory) clazz.newInstance(); + printIt("Instantiated Vendor's AuthConfigFactory"); + } catch (Exception e) { + printIt("Error instantiating vendor's " + "AuthConfigFactory class :" + className); + e.printStackTrace(); + } + } + + return vFactory; + } + + /** + * This method resets the default ACF. + * + */ + public static void resetDefaultACF() throws Exception { + try { + printIt("resetting ACF back to CTS default: " + JASPICData.TSSV_ACF); + AuthConfigFactory origAcf = getAuthConfigFactory(JASPICData.TSSV_ACF); + AuthConfigFactory.setFactory(origAcf); + } catch (Exception ex) { + printIt("Exception while trying to restore original factory class in ACFSwitchFactorys"); + throw ex; + } + } + + /* + * Read the provider configuration XML file and registers each provider with Vendor's default AuthConfigFactory + */ + public static Collection readProviderConfigurationXMLFile(String acpCfgFile) { + Collection providerConfigurationEntriesCollection = null; + + printIt("Reading TestSuite Providers from :" + acpCfgFile); + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + ProviderConfigurationXMLFileProcessor configFileProcessor = new ProviderConfigurationXMLFileProcessor(acpCfgFile); + + // Retrieve the ProviderConfigurationEntries collection + providerConfigurationEntriesCollection = configFileProcessor.getProviderConfigurationEntriesCollection(); + + printIt("TestSuite Providers read successfully " + "from ProviderConfiguration.xml file"); + + return providerConfigurationEntriesCollection; + + } catch (Exception e) { + printIt("Error loading Providers"); + e.printStackTrace(); + } + return null; + + } + + public static void printVerticalIndent() { + printIt("**********************************************************"); + printIt("\n"); + } + + public static void dumpProviders(AuthConfigFactory acf, String[] regIDs) { + + printIt("****************************************************************"); + printIt("******* Dumping AuthConfigFactory registered providers *******"); + for (int ii = 0; ii < regIDs.length; ii++) { + if (regIDs[ii] != null) { + printIt("registrationID[" + ii + "] = " + regIDs[ii]); + AuthConfigFactory.RegistrationContext rc = acf.getRegistrationContext(regIDs[ii]); + printIt("AppContext = " + rc.getAppContext()); + printIt("Message layer = " + rc.getMessageLayer()); + printIt("Description = " + rc.getDescription()); + printIt("isPersistent = " + rc.isPersistent()); + } + } + printIt("****************************************************************"); + printIt(" "); + } + + public static String getRegisteredProviderID(String msgLayer, String appContext, RegistrationListener rl) { + String regID = null; + + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + AuthConfigProvider acp = acf.getConfigProvider(msgLayer, appContext, rl); + + String[] allIDs = acf.getRegistrationIDs(acp); + for (int ii = 0; ii < allIDs.length; ii++) { + AuthConfigFactory.RegistrationContext rc = acf.getRegistrationContext(allIDs[ii]); + if (rc != null) { + String mlayer = rc.getMessageLayer(); + String appctxt = rc.getAppContext(); + if (msgLayer.equals(mlayer) && appContext.equals(appctxt)) { + regID = allIDs[ii]; + break; + } + } else { + printIt("no registrationContext for regID = " + allIDs[ii]); + } + } + + return regID; + } + + public static void printIt(String str) { + System.out.println(str); + } + + public static void printError(String str) { + System.err.println(str); + } + +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java new file mode 100644 index 0000000..0d670db --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/IdUtil.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.TSFileHandler; +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import ee.jakarta.tck.authentication.test.common.logging.server.TSXMLFormatter; +import java.util.Collection; +import java.util.Iterator; +import java.util.logging.Level; + +/** + * + * @author Raja Perumal + */ +public class IdUtil { + + private static ProviderConfigurationXMLFileProcessor configFileProcessor = null; + + private static TSLogger logger = null; + + public IdUtil() { + initializeTSLogger(); + } + + /* + * 1) Reads the provider configuration XML file, 2) Examines providers 3) If possible return a non-null + * ApplicationContextId for a given layer 4) If no suitable ApplicationContextId found it returns an empty string + */ + public static String getAppContextId(String msgLayer) { + String providerConfigFileLocation = System.getProperty("provider.configuration.file"); + + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + configFileProcessor = new ProviderConfigurationXMLFileProcessor(providerConfigFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + Collection providerConfigurationEntriesCollection = configFileProcessor + .getProviderConfigurationEntriesCollection(); + + ProviderConfigurationEntry pce = null; + String appContextId = null; + + Iterator iterator = providerConfigurationEntriesCollection.iterator(); + while (iterator.hasNext()) { + // obtain each ProviderConfigurationEntry and register it + // with TSAuthConfigFactory + pce = iterator.next(); + + if (pce != null) { + appContextId = pce.getApplicationContextId(); + String pceMsgLayer = pce.getMessageLayer(); + + if (msgLayer.equalsIgnoreCase(pceMsgLayer) && !appContextId.equalsIgnoreCase("null")) { + return appContextId; + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + if ((e.getMessage() != null) && (!e.getMessage().equals(""))) { + logger.log(Level.SEVERE, e.getMessage()); + } else { + logger.log(Level.SEVERE, "Error in getAppContextId()"); + } + } + + // if here, we must have had an app contextId that was null or + // unidentifyable msgLayer + return ""; + } + + private static void initializeTSLogger() { + String logFileLocation = null; + if (logger != null) { + return; + } else { + try { + logFileLocation = System.getProperty("log.file.location"); + System.out.println("logFileLocation = " + logFileLocation); + if (logFileLocation != null) { + logger = TSLogger.getTSLogger(JASPICData.LOGGER_NAME); + boolean appendMode = true; + + // create a new file + TSFileHandler fileHandler = new TSFileHandler(logFileLocation + "/" + JASPICData.DEFAULT_LOG_FILE, appendMode); + fileHandler.setFormatter(new TSXMLFormatter()); + logger.addHandler(fileHandler); + } else { + throw new RuntimeException("log.file.location not set"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("TSLogger Initialization failed", e); + } + } + } +} diff --git a/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/JASPICData.java b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/JASPICData.java new file mode 100644 index 0000000..05be33e --- /dev/null +++ b/tck/spi/common/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/JASPICData.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactory; + +/** + * + * @author Sun Microsystems + */ +public class JASPICData { + + // these are the Jakarta Authentication supported profile message-layers + public final static String LAYER_SERVLET = "HttpServlet"; + + public final static String LAYER_SOAP = "SOAP"; + + // this is a name that we will use to internally reference our logger + public final static String LOGGER_NAME = "jsr196"; + + // This log file is used on the appserver side by our cts tests + // to verify success/failures of our tests that involve a need to + // know that status of an exchange between a client and server. + public final static String DEFAULT_LOG_FILE = "authentication-trace-log.xml"; + + public final static String MOD_CLASS_NAME = "moduleClassKey"; + + public final static String SVC_SUBJECT_KEY = "com.sun.ts.tests.jaspic.serviceSubjectKey"; + + // define some statics for the cts AuthConfigFactory implementation and + // the RI's implementation of the AuthConfigFactory + public final static String TSSV_ACF = TSAuthConfigFactory.class.getName(); + + // definitions for Servlet Container Profile (SCP) + public final static String SCP_CONTEXT_PATH = "spitests_servlet_web"; + + public final static String AUTHSTAT_FAILURE_ND = "ModuleAuthStatusFailureNoDispatch"; + + public final static String AUTHSTAT_SENDFAILURE_ND = "ModuleAuthStatusSendFailureNoDispatch"; + + public final static String AUTHSTAT_SENDCONT_ND = "ModuleAuthStatusSendContinueNoDispatch"; + + public final static String AUTHSTAT_SENDSUCCESS_ND = "ModuleAuthStatusSendSuccessNoDispatch"; + + public final static String AUTHSTAT_SUCCESS_ND = "ModuleAuthStatusSuccessNoDispatch"; + + public final static String AUTHSTAT_THROW_EX_ND = "ModuleAuthStatusThrowExNoDispatch"; + + public final static String AUTHSTAT_OPT_SUCCESS = "AuthStatusOptionalSuccess"; + + public final static String AUTHSTAT_MAND_SUCCESS = "AuthStatusMandatorySuccess"; + + public final static String AUTHSTAT_FAILURE_D = "ModuleAuthStatusFailureDispatch"; + + public final static String AUTHSTAT_SENDFAILURE_D = "ModuleAuthStatusSendFailureDispatch"; + + public final static String AUTHSTAT_SENDCONT_D = "ModuleAuthStatusSendContinueDispatch"; + + public final static String AUTHSTAT_SENDSUCCESS_D = "ModuleAuthStatusSendSuccessDispatch"; + + public final static String AUTHSTAT_SUCCESS_D = "ModuleAuthStatusSuccessDispatch"; + + public final static String AUTHSTAT_THROW_EX_D = "ModuleAuthStatusThrowExDispatch"; + +} diff --git a/tck/spi/common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/tck/spi/common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 0000000..7a4388c --- /dev/null +++ b/tck/spi/common/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer \ No newline at end of file diff --git a/tck/spi/common/src/main/webapp/WEB-INF/web.xml b/tck/spi/common/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..180833e --- /dev/null +++ b/tck/spi/common/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + spi_servlet + + + BASIC + default + + + diff --git a/tck/spi/pom.xml b/tck/spi/pom.xml new file mode 100644 index 0000000..09c1019 --- /dev/null +++ b/tck/spi/pom.xml @@ -0,0 +1,41 @@ + + + + + 4.0.0 + + + org.eclipse.ee4j.tck.authentication + jakarta-authentication-tck + 3.1.0-SNAPSHOT + + + spi + pom + + Jakarta Authentication TCK - spi + + + common + + servlet + + soap + + + diff --git a/tck/spi/servlet/auth.conf b/tck/spi/servlet/auth.conf new file mode 100644 index 0000000..6733343 --- /dev/null +++ b/tck/spi/servlet/auth.conf @@ -0,0 +1,34 @@ +reg-entry { + con-entry { + ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigProvider + AuthStatus_SEND_SUCCESS:false + requestPolicy:USER_NAME_PASSWORD + } + reg-ctx { + layer:SOAP + app-ctx:null + description:TestSuite JSR 196 Config Provider + } + reg-ctx { + layer:SOAP + app-ctx:localhost /Hello_web/Hello + description:TestSuite JSR 196 Config Provider + } +} +reg-entry { + con-entry { + ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet + AuthStatus_SEND_SUCCESS:true + requestPolicy:USER_NAME_PASSWORD + } + reg-ctx { + layer:HttpServlet + app-ctx:server /spitests_servlet_web + description:Registration for TSAuthConfigProviderServlet using spitests_servlet_web + } + reg-ctx { + layer:HttpServlet + app-ctx:server /spitests_servlet_web/WrapperServlet + description:Registration for TSAuthConfigProviderServlet using spitests_servlet_web + } +} diff --git a/tck/spi/servlet/pom.xml b/tck/spi/servlet/pom.xml new file mode 100644 index 0000000..75956d2 --- /dev/null +++ b/tck/spi/servlet/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + + org.eclipse.ee4j.tck.authentication + spi + 3.1.0-SNAPSHOT + + + spi.servlet + war + + Jakarta Authentication TCK - Profile SPI Servlet + + A lot of tests about the nitty gritty regarding the SPI per profile. + + + + + org.jakartaee + jaspic-common + 1.0-SNAPSHOT + + + org.eclipse.ee4j.tck.authentication + spi.common + ${project.version} + + + + + spitests_servlet_web + + diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ACFTestServlet.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ACFTestServlet.java new file mode 100644 index 0000000..ca37b21 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ACFTestServlet.java @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.LAYER_SERVLET; + +import ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet; +import jakarta.annotation.security.DeclareRoles; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity(value = @HttpConstraint(rolesAllowed = { "Administrator" }), httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = "Administrator"), + @HttpMethodConstraint(value = "POST", rolesAllowed = "Administrator") }) +@WebServlet(name = "ACFTestServlet", urlPatterns = { "/ACFTestServlet" }) +public class ACFTestServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + private String logFileLocation; + private String servletAppContext; + private String providerConfigFileLocation; + private String vendorACFClass; + private String testMethod; + + private transient CommonTests commonTests; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request, response); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doTests(request, response); + } + + public void doTests(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (Exception ex) { + debug("got exception in ACFTestServlet"); + ex.printStackTrace(); + } + commonTests = new CommonTests(out); + + // get some common props + getPropsAndParams(request, response); + + if (testMethod == null) { + getRegistrationContextId(request, response); + AuthConfigFactoryVerifyPersistence(request, response); + + } else if (testMethod.equals("ACFRemoveRegistration")) { + ACFRemoveRegistration(request, response); + + } else if (testMethod.equals("ACFGetRegistrationIDs")) { + ACFGetRegistrationIDs(request, response); + + } else if (testMethod.equals("ACFGetRegistrationContext")) { + ACFGetRegistrationContext(request, response); + + } else if (testMethod.equals("ACFDetachListener")) { + ACFDetachListener(request, response); + + } else if (testMethod.equals("ACFGetFactory")) { + ACFGetFactory(request, response); + + } else if (testMethod.equals("ACFSwitchFactorys")) { + ACFSwitchFactorys(request, response); + + } else if (testMethod.equals("testACFComesFromSecFile")) { + testACFComesFromSecFile(request, response); + + } else if (testMethod.equals("ACFPersistentRegisterOnlyOneACP")) { + ACFPersistentRegisterOnlyOneACP(request, response); + + } else if (testMethod.equals("ACFInMemoryRegisterOnlyOneACP")) { + ACFInMemoryRegisterOnlyOneACP(request, response); + + } else if (testMethod.equals("ACFUnregisterACP")) { + ACFUnregisterACP(request, response); + + } else if (testMethod.equals("AuthConfigFactoryRegistration")) { + AuthConfigFactoryRegistration(request, response); + + } else if (testMethod.equals("ACFInMemoryNotifyOnUnReg")) { + ACFInMemoryNotifyOnUnReg(request, response); + + } else if (testMethod.equals("ACFPersistentNotifyOnUnReg")) { + ACFPersistentNotifyOnUnReg(request, response); + + } else if (testMethod.equals("ACFInMemoryPrecedenceRules")) { + ACFInMemoryPrecedenceRules(request, response); + + } else if (testMethod.equals("ACFPersistentPrecedenceRules")) { + ACFPersistentPrecedenceRules(request, response); + + } else if (testMethod.equals("ACFRemoveRegistrationWithBadId")) { + ACFRemoveRegistrationWithBadId(request, response); + } + + // restore original (CTS) factory class + // note: this should be done in many of the calls but its a safety measure + // to ensure we are resetting things back to expected defaults + try { + CommonUtils.resetDefaultACF(); + } catch (Exception ex) { + debug("ACFTestServlet: error calling CommonUtils.resetDefaultACF(): " + ex.getMessage()); + ex.printStackTrace(); + } + } + + private void getPropsAndParams(HttpServletRequest request, HttpServletResponse response) { + + // set logfile location + logFileLocation = System.getProperty("log.file.location"); + if ((logFileLocation != null) && (-1 < logFileLocation.indexOf(JASPICData.DEFAULT_LOG_FILE))) { + // if here, we have logfile location value which contains + // JASPICData.DEFAULT_LOG_FILE + debug("logFileLocation already set"); + } else { + debug("logFileLocation NOT set completely"); + System.setProperty("log.file.location", logFileLocation); + } + debug("logFileLocation = " + logFileLocation); + + // set provider config file + providerConfigFileLocation = System.getProperty("provider.configuration.file"); + debug("TS Provider ConfigFile = " + providerConfigFileLocation); + if (providerConfigFileLocation == null) { + debug("ERROR: getPropsAndParams(): providerConfigFileLocation = null"); + } else { + debug("getPropsAndParams(): providerConfigFileLocation = " + providerConfigFileLocation); + } + + // set testMethod + testMethod = request.getParameter("method.under.test"); + + // set vendor class + vendorACFClass = System.getProperty("vendor.authconfig.factory"); + if (vendorACFClass == null) { + debug("ERROR: getPropsAndParams(): vendorACFClass = null"); + } else { + debug("getPropsAndParams(): vendorACFClass = " + vendorACFClass); + } + + servletAppContext = IdUtil.getAppContextId(JASPICData.LAYER_SERVLET); + + return; + } + + /* + * This is testing that acf.removeRegistration(arg) will return FALSE when invalid arg supplied. (this requirement + * described in javadoc for api) + */ + public void ACFRemoveRegistration(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + System.out.println("in ACFTestServlet.ACFRemoveRegistration"); + commonTests._ACF_testFactoryRemoveRegistration(); + System.out.println("in ACFTestServlet ... just called commonTests._ACF_testFactoryRemoveRegistration"); + + out = response.getWriter(); + if (out != null) { + System.out.println("in ACFTestServlet ... out != null so passed"); + out.println("ACFTestServlet->ACFRemoveRegistration() passed"); + out.flush(); + } else { + System.out.println("in ACFTestServlet ... out == null so failed"); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFRemoveRegistration() failed"); + ex.printStackTrace(); + } + } + + /* + * This is testing that acf.getRegistrationIDs(acp) NEVER returns null hint: this must return empty array even if + * unrecognized acp. (this requirement described in javadoc for api) + */ + public void ACFGetRegistrationIDs(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + commonTests._ACF_testFactoryGetRegistrationIDs(); + + out = response.getWriter(); + if (out != null) { + out.println("ACFTestServlet->ACFGetRegistrationIDs() passed"); + out.flush(); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFGetRegistrationIDs() failed"); + ex.printStackTrace(); + } + } + + /* + * This is testing that acf.getRegistrationContext(string) returns NULL for an unrecognized string (this requirement + * described in javadoc for api) + */ + public void ACFGetRegistrationContext(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + commonTests._ACF_testFactoryGetRegistrationContext(); + + out = response.getWriter(); + if (out != null) { + out.println("ACFTestServlet->ACFGetRegistrationContext() passed"); + out.flush(); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFGetRegistrationContext() failed"); + ex.printStackTrace(); + } + } + + /* + * This is testing that acf.detachListener(...) returns non-NULL for an unfound listner (this requirement described in + * javadoc for api) + */ + public void ACFDetachListener(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + commonTests._ACF_testFactoryDetachListener(vendorACFClass); + + out = response.getWriter(); + if (out != null) { + out.println("ACFTestServlet->ACFDetachListener() passed"); + out.flush(); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFDetachListener() failed"); + ex.printStackTrace(); + } + } + + public void ACFGetFactory(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + commonTests._ACF_getFactory(); + + out = response.getWriter(); + if (out != null) { + out.println("ACFTestServlet->ACFGetFactory() passed"); + out.flush(); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACF_getFactory() failed"); + ex.printStackTrace(); + } + } + + public void ACFSwitchFactorys(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._ACFSwitchFactorys(vendorACFClass); + + if (out != null) { + out.println("ACFTestServlet->ACFSwitchFactorys() passed"); + out.flush(); + } + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFSwitchFactorys() failed"); + ex.printStackTrace(); + } + } + + public void testACFComesFromSecFile(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._testACFComesFromSecFile(); + out.println("ACFTestServlet->testACFComesFromSecFile() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->testACFComesFromSecFile() failed"); + ex.printStackTrace(); + } + } + + public void ACFPersistentRegisterOnlyOneACP(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, true); + out.println("ACFTestServlet->ACFPersistentRegisterOnlyOneACP() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFPersistentRegisterOnlyOneACP() failed"); + ex.printStackTrace(); + } + } + + public void ACFInMemoryRegisterOnlyOneACP(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, false); + out.println("ACFTestServlet->ACFInMemoryRegisterOnlyOneACP() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFInMemoryRegisterOnlyOneACP() failed"); + ex.printStackTrace(); + } + } + + public void ACFUnregisterACP(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._ACFUnregisterACP(logFileLocation, providerConfigFileLocation, vendorACFClass); + out.println("ACFTestServlet->ACFUnregisterACP() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFUnregisterACP() failed"); + ex.printStackTrace(); + } + } + + /* + * This is verifying that the invocation of removeRegistration() with an invalid and non-existant regId will return + * false. + */ + public void ACFRemoveRegistrationWithBadId(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + commonTests._ACFRemoveRegistrationWithBadId(); + out.println("ACFTestServlet->ACFRemoveRegistrationWithBadId() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFRemoveRegistrationWithBadId() failed"); + ex.printStackTrace(); + } + } + + /** + * 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + */ + public void AuthConfigFactoryRegistration(HttpServletRequest request, HttpServletResponse response) { + + PrintWriter out = null; + try { + out = response.getWriter(); + AuthConfigFactory registerACF = CommonUtils.register(logFileLocation, providerConfigFileLocation, vendorACFClass); + + if (registerACF != null) { + out.println("ACFTestServlet->AuthConfigFactoryRegistration() passed"); + } else { + out.println("ACFTestServlet->AuthConfigFactoryRegistration() failed"); + } + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->AuthConfigFactoryRegistration() failed"); + ex.printStackTrace(); + } + } + + /** + * + */ + public void ACFInMemoryNotifyOnUnReg(HttpServletRequest request, HttpServletResponse response) { + + PrintWriter out = null; + try { + out = response.getWriter(); + + // test using in-memory registration + commonTests._ACFTestNotifyOnUnReg(vendorACFClass, false); + + out.println("ACFTestServlet->ACFInMemoryNotifyOnUnReg() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFInMemoryNotifyOnUnReg() failed"); + ex.printStackTrace(); + } + } + + /** + * + */ + public void ACFPersistentNotifyOnUnReg(HttpServletRequest request, HttpServletResponse response) { + + PrintWriter out = null; + try { + out = response.getWriter(); + + // now test using persistent registration + commonTests._ACFTestNotifyOnUnReg(vendorACFClass, true); + + out.println("ACFTestServlet->ACFPersistentNotifyOnUnReg() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFPersistentNotifyOnUnReg() failed"); + ex.printStackTrace(); + } + } + + /** + * + */ + public void ACFInMemoryPrecedenceRules(HttpServletRequest request, HttpServletResponse response) { + + PrintWriter out = null; + try { + out = response.getWriter(); + + // test using in-memory registration + commonTests._ACFTestPrecedenceRules(vendorACFClass, false); + + out.println("ACFTestServlet->ACFInMemoryPrecedenceRules() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFInMemoryPrecedenceRules() failed"); + ex.printStackTrace(); + } + } + + /** + * + */ + public void ACFPersistentPrecedenceRules(HttpServletRequest request, HttpServletResponse response) { + + PrintWriter out = null; + try { + out = response.getWriter(); + + // now test using persistent registration + commonTests._ACFTestPrecedenceRules(vendorACFClass, true); + + out.println("ACFTestServlet->ACFPersistentPrecedenceRules() passed"); + out.flush(); + } catch (Exception ex) { + System.out.println("ACFTestServlet->ACFPersistentPrecedenceRules() failed"); + ex.printStackTrace(); + } + } + + /** + * + * 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description This will use an appContext value that was used to register a provider, and it will see if it can use the + * AuthConfigFactory.RegistrationContext API to try and access the same appContext value that was used during the + * registration process. + * + */ + public void getRegistrationContextId(HttpServletRequest request, HttpServletResponse response) { + String appContext = "localhost /Hello_web/Hello"; + + PrintWriter out = null; + try { + out = response.getWriter(); + + // register providers in vendor factory + AuthConfigFactory registeredACF = CommonUtils.register(logFileLocation, providerConfigFileLocation, vendorACFClass); + if (registeredACF == null) { + out.println("getRegistrationContextId failed"); + } + + // verify we can access a given provider (any provider) appcontext id + boolean bVerified = false; + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + String[] regIDs = acf.getRegistrationIDs(null); + for (int ii = 0; ii < regIDs.length; ii++) { + // loop through the ACF's registration ids + + if (regIDs[ii] != null) { + AuthConfigFactory.RegistrationContext acfReg; + acfReg = acf.getRegistrationContext(regIDs[ii]); + if (acfReg != null) { + debug("appContext = " + appContext); + debug("acfReg.getAppContext() = " + acfReg.getAppContext()); + debug("layer = " + acfReg.getMessageLayer()); + String str = acfReg.getAppContext(); + if ((str != null) && (str.equals(appContext))) { + // we found our provider info + debug("Found it : RegistrationID for our ACP=" + regIDs[ii]); + bVerified = true; + break; + } + } + } + } + + if (!bVerified) { + String msg = "Could not find appContext=" + appContext; + msg += " in the ACF's list of registration id info"; + debug(msg); + out.println("getRegistrationContextId() TSProviders registration failed"); + } + + out.println("getRegistrationContextId() TSProviders registration passed"); + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory() or "; + msg = msg + "ACF.setFactory(). You may need to explicitly set your server side security policies."; + if (out == null) { + System.out.println(msg); + } else { + out.println(msg); + } + ex.printStackTrace(); + } catch (Exception ex) { + System.out.println("getRegistrationContextId() TSProviders registration failed"); + ex.printStackTrace(); + } + } + + /** + * + * 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Load vendor's AuthConfigFactory and make sure the registered providers return properly for the right message layer + * and appContextId + * + * Note: We test the persistance behaviour for vendor's AuthConfigFactory by registering providers from a persisted + * file, then we verify the registrations went correctly. + * + */ + public void AuthConfigFactoryVerifyPersistence(HttpServletRequest request, HttpServletResponse response) { + boolean verified = false; + + PrintWriter out = null; + try { + out = response.getWriter(); + + // Register providers in vendor factory + AuthConfigFactory registerACF = CommonUtils.register(logFileLocation, providerConfigFileLocation, vendorACFClass); + if (registerACF != null) { + out.println("AuthConfigFactoryVerifyPersistence failed"); + } + + // Get system default AuthConfigFactory + AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory(); + + if (authConfigFactory != null) { + debug("Default ACF class name = " + authConfigFactory.getClass().getName()); + verified = verifyRegistrations(authConfigFactory); + } else { + out.println("AuthConfigFactoryVerifyPersistence() failed"); + debug("Default ACF is null" + " can't verify registrations for TestSuite Providers"); + } + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " You may need to explicitly set your server side security policies."; + debug(msg); + ex.printStackTrace(); + + } catch (Exception e) { + System.out.println("AuthConfigFactoryVerifyPersistence() failed"); + e.printStackTrace(); + } + + if (verified && out != null) { + out.println("AuthConfigFactoryVerifyPersistence() passed"); + } + } + + private boolean verifyRegistrations(AuthConfigFactory authConfigFactory) { + debug("Verifying Provider Registrations ..."); + + try { + // Get AuthConfigProviderServlet + AuthConfigProvider servletAuthConfigProvider = authConfigFactory.getConfigProvider(LAYER_SERVLET, servletAppContext, null); + + if (servletAuthConfigProvider != null) { + if (servletAuthConfigProvider.getClass().getName().equals(TSAuthConfigProviderServlet.class.getName())) { + debug("TSAuthConfigProviderServlet registered for" + " message layer=HttpServlet" + " and appContextId=" + + servletAppContext); + } else { + debug("Wrong provider registerd for " + " message layer=HttpServlet" + " and appContextId=" + servletAppContext); + return false; + } + + } else { + debug("Error : No AuthConfigprovider registerd for" + " message layer=HttpServlet" + " and appContextId=" + + servletAppContext); + return false; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AnotherMandatoryAuthen.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AnotherMandatoryAuthen.java new file mode 100644 index 0000000..66aab40 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AnotherMandatoryAuthen.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity( + value = + @HttpConstraint(rolesAllowed = { "Manager" }), + httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = "Manager"), + @HttpMethodConstraint(value = "POST", rolesAllowed = "Manager") }) +@WebServlet(name = "AnotherMandatoryAuthen", urlPatterns = { "/AnotherMandatoryAuthen" }) +public class AnotherMandatoryAuthen extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request, response); + printOut("Enterred AnotherMandatoryAuthen->doGet()", response); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In AnotherMandatoryAuthen->doPost()"); + + printOut("Enterred AnotherMandatoryAuthen->doPost()", response); + printOut("request.getServletPath() = " + request.getServletPath(), response); + printOut("request.getPathInfo() = " + request.getPathInfo(), response); + printOut("request.getMethod() = " + request.getMethod(), response); + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In AnotherMandatoryAuthen->service()"); + printOut("Enterred AnotherMandatoryAuthen->service()", response); + } + + private void printOut(String str, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (Exception ex) { + ex.printStackTrace(); + out = null; + } + + if (out != null) { + out.println(str); + } else { + System.out.println(str); + } + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java new file mode 100644 index 0000000..2fc4315 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthFactoryContainerInitializer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authentication.test.basic.servlet; + +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import java.util.Set; + +public class AuthFactoryContainerInitializer implements ServletContainerInitializer { + + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + AuthConfigFactory.setFactory(new TSAuthConfigFactory()); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthStatusMandatorySuccess.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthStatusMandatorySuccess.java new file mode 100644 index 0000000..99f5868 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/AuthStatusMandatorySuccess.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity( + value = + @HttpConstraint(rolesAllowed = { "Administrator" }), + httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = "Administrator"), + @HttpMethodConstraint(value = "POST", rolesAllowed = "Administrator") }) +@WebServlet(name = "AuthStatusMandatorySuccess", urlPatterns = { "/AuthStatusMandatorySuccess" }) +public class AuthStatusMandatorySuccess extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = response.getWriter(); + doPost(request, response); + out.println("Enterred AuthStatusMandatorySuccess->doGet()"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In AuthStatusMandatorySuccess->doPost()"); + doTests(request, response); + } + + public void doTests(HttpServletRequest request, HttpServletResponse response) { + System.out.println("In AuthStatusMandatorySuccess->doTests()"); + + PrintWriter out = null; + try { + out = response.getWriter(); + + // get some common props + String testMethod = request.getParameter("method.under.test"); + + if (testMethod == null) { + + out.println("request.getServletPath() = " + request.getServletPath()); + out.println("request.getPathInfo() = " + request.getPathInfo()); + out.println("request.getMethod() = " + request.getMethod()); + + } else if (testMethod.equals("testSecRespCalledAfterSvcInvoc")) { + _testSecRespCalledAfterSvcInvoc(request, response); + } + } catch (Exception ex) { + System.out.println("WrapperServlet->testRequestWrapper() failed"); + ex.printStackTrace(); + } + } + + /* + * This should be satisfying assertion JASPIC:SPEC:108. + */ + public void _testSecRespCalledAfterSvcInvoc(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + + out.println("Enterred AuthStatusMandatorySuccess->_testSecRespCalledAfterSvcInvoc()"); + System.out.println("In AuthStatusMandatorySuccess->_testSecRespCalledAfterSvcInvoc()"); + + // see if a cts proprietary requestAttribute was set in the secureResponse + // call and if so, we have a problem. The secure response should be called + // AFTER this servlet invocation thus we should not see this attribute set + // yet. If set, it means our secureResponse was called BEFOR the service + // invocation and thats violation of jaspic 1.1. spec (section 3.8.3.2) + // also + // see https://java.net/jira/browse/JASPIC_SPEC-9 + String wasSecureResponseInvokedYet = (String) request.getAttribute("secureRespCalled"); + if ((wasSecureResponseInvokedYet != null) && (wasSecureResponseInvokedYet.equalsIgnoreCase("true"))) { + // error - the "secureRespCalled" attribute was set and that should + // only occur in secureResponse which indicates a problem since the + // secureResponse should not get called before service invocation. + System.out.println("ERROR - secureResponse called before service invocation"); + out.println("testSecRespCalledAfterSvcInvoc() failed"); + return; + } else if (wasSecureResponseInvokedYet == null) { + // This is good and validates assertion: JASPIC:SPEC:108 + // The attribue was NOT set in secureResponse, so it must not + // have been called before the service invocation of this servlet + System.out.println( + "_testSecRespCalledAfterSvcInvoc() secureResponse NOT called before service invocation - which is correct!"); + out.println("testSecRespCalledAfterSvcInvoc() passed"); + } + + String wasValidateRequestInvokedYet = (String) request.getAttribute("validateReqCalled"); + if ((wasValidateRequestInvokedYet == null) || (!wasValidateRequestInvokedYet.equalsIgnoreCase("true"))) { + // ERROR - the only reason we should be here is if there was a Multi-msg + // dialog + // (per spec 3.8.3) but that should NOT be the case thus we should NOT + // get in here as caller identity should've been established in + // validateRequest already. + System.out.println("ERROR - validateRequest NOT called before service invocation"); + out.println("testSecRespCalledAfterSvcInvoc() failed"); + return; + } else { + // good - caller identity was established in validateRequest already, + // which + // is why we correctly see "validateReqCalled" request attribute was + // set. + System.out.println("_testSecRespCalledAfterSvcInvoc() validateRequest was correctly called BEFORE service invocation"); + out.println("testSecRespCalledAfterSvcInvoc() passed"); + } + + out.flush(); + } catch (Exception ex) { + System.out.println("AuthStatusMandatorySuccess->_testSecRespCalledAfterSvcInvoc() failed"); + ex.printStackTrace(); + } + + return; + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java new file mode 100644 index 0000000..2977ff9 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/ModTestServlet.java @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.Principal; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity( + value = + @HttpConstraint(rolesAllowed = { "Administrator" }), + httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = "Administrator"), + @HttpMethodConstraint(value = "POST", rolesAllowed = "Administrator") }) +@WebServlet(name = "ModTestServlet", urlPatterns = { "/ModTestServlet" }) +public class ModTestServlet extends HttpServlet { + + private String logFileLocation; + private String servletAppContext = null; + private String providerConfigFileLocation; + private String testMethod = null; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->doGet()"); + + doTests(request, response); + + out.println("Laving ModTestServlet->doGet()"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In ModTestServlet->doPost()"); + + doTests(request, response); + + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->doPost()"); + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In ModTestServlet->service()"); + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->service()"); + + super.service(request, response); + } + + public void doTests(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (Exception ex) { + debug("got exception in ModTestServlet"); + ex.printStackTrace(); + } + + // get some common props that are passed into our servlet request + getPropsAndParams(request, response); + + if (testMethod.equals("testAuthenResultsOnHttpServlet")) { + MTSAuthenResultsOnHttpServlet(request, response); + } else if (testMethod.equals("testAuthenIsUserInRole")) { + MTSAuthenIsUserInRole(request, response); + } else if (testMethod.equals("testRemoteUserCorrespondsToPrin")) { + MTSRemoteUserCorrespondsToPrin(request, response); + } else if (testMethod.equals("testAuthenAfterLogout")) { + testAuthenAfterLogout(request, response); + } else if (testMethod.equals("testGPCIsUserInRole")) { + testGPCIsUserInRole(request, response); + } else if (testMethod.equals("testGPCGetUserPrincipal")) { + testGPCGetUserPrincipal(request, response); + } else if (testMethod.equals("testGPCGetRemoteUser")) { + testGPCGetRemoteUser(request, response); + } else if (testMethod.equals("testGPCGetAuthType")) { + testGPCGetAuthType(request, response); + } else { + // if here, it most likely means that we ran a test from within + // Client.java which + // invoked this servlet with the only intent being to check if access + // could be + // made. Some tests only care that they can access this servlet and do NOT + // need to run any particular testMethod in here. + debug("WARNING: ModTestServlet,doTests() testMethod not recognized: " + testMethod); + } + } + + // this pulls out some params that are passed in on our servlet req + // that is made from within the client code (e.g. Client.java). + private void getPropsAndParams(HttpServletRequest req, HttpServletResponse response) { + + // set logfile location + logFileLocation = req.getParameter("log.file.location"); + if ((logFileLocation != null) && (-1 < logFileLocation.indexOf(JASPICData.DEFAULT_LOG_FILE))) { + // if here, we have logfile location value which contains + // JASPICData.DEFAULT_LOG_FILE + debug("logFileLocation already set"); + } else if (logFileLocation != null) { + debug("logFileLocation NOT set completely"); + System.setProperty("log.file.location", logFileLocation); + } else { + debug("ModTestServlet: logFileLocation null"); + } + debug("logFileLocation = " + logFileLocation); + + // set provider config file + providerConfigFileLocation = req.getParameter("provider.configuration.file"); + debug("TS Provider ConfigFile = " + providerConfigFileLocation); + if (providerConfigFileLocation == null) { + debug("ERROR: getPropsAndParams(): providerConfigFileLocation = null"); + } else { + debug("getPropsAndParams(): providerConfigFileLocation = " + providerConfigFileLocation); + } + + // set testMethod + testMethod = req.getParameter("method.under.test"); + + servletAppContext = IdUtil.getAppContextId(JASPICData.LAYER_SERVLET); + + return; + } + + /* + * This should be satisfying assertion JASPIC:SPEC:322. + */ + public void MTSAuthenResultsOnHttpServlet(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->MTSAuthenResultsOnHttpServlet()"); + System.out.println("In ModTestServlet->MTSAuthenResultsOnHttpServlet()"); + + // after successful login, we should see non-null values for all of these! + String strAuthType = request.getAuthType(); + String strRemoteUser = request.getRemoteUser(); + Principal pp = request.getUserPrincipal(); + + if ((strAuthType != null) && (strRemoteUser != null) && (pp != null)) { + out.println("request.getAuthType() = " + strAuthType); + out.println("request.getRemoteUser() = " + strRemoteUser); + out.println("request.getUserPrincipal() = " + pp.toString()); + out.println("Authentication results on HttpServletRequest ok."); + out.println("ModTestServlet->testAuthenResultsOnHttpServlet() passed"); + } else if ((strRemoteUser == null) && (pp == null) && (strAuthType == null)) { + out.println("request.getRemoteUser() == pp == null"); + out.println("ModTestServlet->testAuthenResultsOnHttpServlet() passed"); + } else { + out.println("Authentication results on HttpServletRequest Error."); + out.println("request.getAuthType() = " + strAuthType); + out.println("request.getRemoteUser() = " + strRemoteUser); + if (pp != null) { + out.println("request.getUserPrincipal() = " + pp.toString()); + } else { + out.println("request.getUserPrincipal() = null"); + } + } + } catch (Exception ex) { + System.out.println("ModTestServlet->MTSAuthenResultsOnHttpServlet() failed"); + ex.printStackTrace(); + } + + return; + } // MTSAuthenResultsOnHttpServlet() + + /* + * This is basically the same thing as MTSAuthenResultsOnHttpServlet() but we are returning slightly different return + * values so... + */ + public void testAuthenAfterLogout(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->testAuthenAfterLogout()"); + debug("In ModTestServlet->testAuthenAfterLogout()"); + + // after successful login, we must get a non-null + // value for getUserPrincipal() + Principal pp = request.getUserPrincipal(); + + if (pp != null) { + // good - pp was not null which indicates successful authN + out.println("ModTestServlet->testAuthenAfterLogout() passed"); + } else { + // oh oh - we got null pp which indicates we are not authenticated + out.println("testAuthenAfterLogout() failed - getUserPrincipal() returned null."); + } + } catch (Exception ex) { + debug("ModTestServlet->testAuthenAfterLogout() failed"); + ex.printStackTrace(); + } + + return; + } + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void MTSAuthenIsUserInRole(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->MTSAuthenIsUserInRole()"); + System.out.println("In ModTestServlet->MTSAuthenIsUserInRole()"); + + // after successful login, we should see true value for isUserInRole + boolean bval = request.isUserInRole("Administrator"); + + if (bval == true) { + debug(out, "isUserInRole() returns true."); + out.println("ModTestServlet->testAuthenIsUserInRole() passed"); + } else { + debug(out, "Authentication Error - isUserInRole() returns false."); + debug(out, "request.getRemoteUser() = " + request.getRemoteUser()); + Principal pp = request.getUserPrincipal(); + if (pp != null) { + debug(out, "request.getUserPrincipal().getName() = " + pp.getName()); + } else { + debug(out, "request.getUserPrincipal() == null"); + } + out.println("ModTestServlet->MTSAuthenIsUserInRole() failed"); + } + } catch (Exception ex) { + System.out.println("ModTestServlet->MTSAuthenIsUserInRole() failed"); + ex.printStackTrace(); + } + + return; + } // MTSAuthenIsUserInRole() + + /* + * (Per jsr-196 spec v1.1 section 3.8.4) This will get called after we have authenticated with our user j2ee in role + * Administrator. When we get into this test method, a call to getUserPrincipal(), getRemoteUser(), and to + * getUserPrincipal().getName() should all CORRESPOND. "Correspond" is not a hard term as it implies there could be some + * user mappings involved. + * + * (Using Servlet spec v3.1, section 13.3 - it states: "The getRemoteUser method returns the name of the remote user" + * AND "Calling the getName method on the Principal returned by getUserPrincipal returns the name of the remote user." + * We can use these two Servlet spec references combined with our JASPIC reference to validate that principal.getName() + * equals the same value returned by getRemoteUser(). + */ + public void MTSRemoteUserCorrespondsToPrin(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->MTSRemoteUserCorrespondsToPrin()"); + System.out.println("In ModTestServlet->MTSRemoteUserCorrespondsToPrin()"); + + // after successful login, we should see true value for isUserInRole + String strRemoteUser = request.getRemoteUser(); + Principal pp = request.getUserPrincipal(); + String prinName = null; + if (pp != null) { + prinName = pp.getName(); + } + + if ((prinName != null) && (strRemoteUser != null) && prinName.equals(strRemoteUser)) { + out.println("principal.getName() = " + prinName); + out.println("request.getRemoteUser() = " + strRemoteUser); + out.println("ModTestServlet->testRemoteUserCorrespondsToPrin() passed"); + } else { + out.println("ModTestServlet->MTSRemoteUserCorrespondsToPrin() failed"); + out.println("principal.getName() = " + prinName); + out.println("request.getRemoteUser() = " + strRemoteUser); + if (pp != null) { + out.println("request.getUserPrincipal().toString() = " + pp.toString()); + } else { + out.println("request.getUserPrincipal() = null"); + } + } + + } catch (Exception ex) { + System.out.println("ModTestServlet->MTSRemoteUserCorrespondsToPrin() failed"); + ex.printStackTrace(); + } + + return; + } // MTSRemoteUserCorrespondsToPrin() + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void testGPCIsUserInRole(HttpServletRequest request, HttpServletResponse response) { + try { + boolean bval = false; + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->testGPCIsUserInRole()"); + + // first make sure user is in role! + // after successful login, we should see true value for isUserInRole + bval = request.isUserInRole("Administrator"); + if (bval == true) { + debug(out, "isUserInRole() returns true."); + } else { + debug(out, "Authentication Error - isUserInRole() returns false."); + debug(out, "request.getRemoteUser() = " + request.getRemoteUser()); + out.println("ModTestServlet->testGPCIsUserInRole() failed"); + } + + if (bval) { + out.println("ModTestServlet->testGPCIsUserInRole() passed"); + } + + } catch (Exception ex) { + System.out.println("ModTestServlet->testGPCIsUserInRole() failed"); + ex.printStackTrace(); + } + + return; + } + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void testGPCGetUserPrincipal(HttpServletRequest request, HttpServletResponse response) { + try { + boolean bval = false; + + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->testGPCGetUserPrincipal()"); + + // getUserPrincipal() should have been set on proper validateRequest() + if (request.getUserPrincipal() == null) { + debug(out, "ERROR - request.getUserPrincipal() == null but should not!"); + } + + String principalName = request.getUserPrincipal().getName(); + if (principalName != null) { + debug(out, "request.getUserPrincipal().getName() = " + principalName); + bval = true; + } else { + debug(out, "ERROR - request.getUserPrincipal().getName() == null but should not be"); + out.println("ModTestServlet->testGPCGetUserPrincipal() failed"); + } + + if (bval) { + out.println("ModTestServlet->testGPCGetUserPrincipal() passed"); + } + + } catch (Exception ex) { + System.out.println("ModTestServlet->testGPCGetUserPrincipal() failed"); + ex.printStackTrace(); + } + + return; + } + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void testGPCGetRemoteUser(HttpServletRequest request, HttpServletResponse response) { + try { + boolean bval = false; + + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->testGPCGetRemoteUser()"); + + // getRemoteUser() should have been set on proper validateRequest() + String remoteUser = request.getRemoteUser(); + if (remoteUser != null) { + debug(out, "request.getRemoteUser() = " + remoteUser); + bval = true; + } else { + debug(out, "ERROR - request.getRemoteUser() == null but should not be"); + out.println("ModTestServlet->testGPCGetRemoteUser() failed"); + } + + if (bval) { + out.println("ModTestServlet->testGPCGetRemoteUser() passed"); + } + + } catch (Exception ex) { + System.out.println("ModTestServlet->testGPCGetRemoteUser() failed"); + ex.printStackTrace(); + } + + return; + } + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void testGPCGetAuthType(HttpServletRequest request, HttpServletResponse response) { + try { + boolean bval = false; + + PrintWriter out = response.getWriter(); + out.println("Enterred ModTestServlet->testGPCGetAuthType()"); + debug("enterred ModTestServlet->testGPCGetAuthType()"); + + // getAuthType() should have been set on proper validateRequest() + String authType = request.getAuthType(); + if (authType != null) { + debug(out, "request.getAuthType() = " + authType); + bval = true; + } else { + debug(out, "ERROR - request.getAuthType() == null but should not be"); + out.println("ModTestServlet->testGPCGetAuthType() failed"); + } + + if (bval) { + out.println("ModTestServlet->testGPCGetAuthType() passed"); + } + + } catch (Exception ex) { + System.out.println("ModTestServlet->testGPCGetAuthType() failed"); + ex.printStackTrace(); + } + + return; + } + + public void debug(PrintWriter out, String str) { + out.println(str); + System.out.println(str); + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OpenToAllServlet.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OpenToAllServlet.java new file mode 100644 index 0000000..a21ef11 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OpenToAllServlet.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.Principal; + +@ServletSecurity +@WebServlet(name = "OpenToAllServlet", urlPatterns = { "/OpenToAllServlet" }) +public class OpenToAllServlet extends HttpServlet { + + private static final long serialVersionUID = 1l; + + private String logFileLocation; + private String servletAppContext = null; + private String providerConfigFileLocation; + private String testMethod = null; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request, response); + debug("Enterred OpenToAllServlet->doGet()"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + debug("Enterred OpenToAllServlet->doPost()"); + + doTests(request, response); + } + + public void doTests(HttpServletRequest request, HttpServletResponse response) { + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (Exception ex) { + debug("got exception in ModTestServlet"); + ex.printStackTrace(); + } + + // get some common props that are passed into our servlet request + getPropsAndParams(request, response); + + if (testMethod.equals("testGPCWithNoRequiredAuth")) { + TestGPCWithNoRequiredAuth(request, response); + } else if (testMethod.equals("testAuthenAfterLogout")) { + TestAuthenAfterLogout(request, response); + } else { + // if here, it most likely means that we ran a test from within + // Client.java which + // invoked this servlet with the only intent being to check if access + // could be + // made. Some tests only care that they can access this servlet and do NOT + // need to run any particular testMethod in here. + debug("WARNING: OpenToAllServlet.doTests() testMethod not recognized: " + testMethod); + } + } + + public void TestAuthenAfterLogout(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + System.out.println("In OpenToAllServlet->TestAuthenAfterLogout()"); + + Principal pp = request.getUserPrincipal(); + if (pp != null) { + // ohoh - when no authN required for this servlet, we are detecting + // that some authentication occurred -OR- that we are pre-logged in + // either way, thats a problem so force a logout. + out.println("OpenToAllServlet: we should not be authenticated so forcing logout"); + request.logout(); + } else { + out.println("OpenToAllServlet: pp == null so we were not pre-logged in."); + out.println("no need to force a logout since we are (correctly) not logged in."); + } + } catch (Exception ex) { + System.out.println("OpenToAllServlet->TestAuthenAfterLogout() failed"); + ex.printStackTrace(); + } + } + + /* + * This will get called after we have authenticated with our user j2ee in role Administrator. When we get into this test + * method, a call to isUserInRole better return true. if we have not authenticated, we expect to get false. + */ + public void TestGPCWithNoRequiredAuth(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("Enterred OpenToAllServlet->TestGPCWithNoRequiredAuth()"); + System.out.println("In OpenToAllServlet->TestGPCWithNoRequiredAuth()"); + + // after successful login, we should see true value for isUserInRole + boolean bval = request.isUserInRole("Administrator"); + + if (bval == true) { + debug(out, "isUserInRole() returns true."); + debug("OpenToAllServlet->testAuthenIsUserInRole() isUserInRole() returns true"); + } else { + debug("OpenToAllServlet->TestGPCWithNoRequiredAuth() isUserInRole() returns false"); + } + out.println("OpenToAllServlet->testAuthenIsUserInRole() passed"); + + } catch (Exception ex) { + System.out.println("OpenToAllServlet->TestGPCWithNoRequiredAuth() failed"); + ex.printStackTrace(); + } + + return; + } + + // this pulls out some params that are passed in on our servlet req + // that is made from within the client code (e.g. Client.java). + private void getPropsAndParams(HttpServletRequest req, HttpServletResponse response) { + + // set logfile location + logFileLocation = req.getParameter("log.file.location"); + if ((logFileLocation != null) && (-1 < logFileLocation.indexOf(JASPICData.DEFAULT_LOG_FILE))) { + // if here, we have logfile location value which contains + // JASPICData.DEFAULT_LOG_FILE + debug("logFileLocation already set"); + } else if (logFileLocation != null) { + debug("logFileLocation NOT set completely"); + System.setProperty("log.file.location", logFileLocation); + } else { + debug("ModTestServlet: logFileLocation null"); + } + debug("logFileLocation = " + logFileLocation); + + // set provider config file + providerConfigFileLocation = req.getParameter("provider.configuration.file"); + debug("TS Provider ConfigFile = " + providerConfigFileLocation); + if (providerConfigFileLocation == null) { + debug("ERROR: getPropsAndParams(): providerConfigFileLocation = null"); + } else { + debug("getPropsAndParams(): providerConfigFileLocation = " + providerConfigFileLocation); + } + + // set testMethod + testMethod = req.getParameter("method.under.test"); + + servletAppContext = IdUtil.getAppContextId(JASPICData.LAYER_SERVLET); + + return; + } + + public void debug(PrintWriter out, String str) { + out.println(str); + System.out.println(str); + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OptionalAuthen.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OptionalAuthen.java new file mode 100644 index 0000000..83d6d59 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/OptionalAuthen.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity(httpMethodConstraints = { @HttpMethodConstraint(value = "GET"), @HttpMethodConstraint(value = "POST") }) +@WebServlet(urlPatterns = { "/OptionalAuthen", "/ModuleAuthStatusThrowExNoDispatch" }) +public class OptionalAuthen extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = response.getWriter(); + doPost(request, response); + out.println("Enterred OptionalAuthen->doGet()"); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In OptionalAuthen->doPost()"); + + PrintWriter out = response.getWriter(); + out.println("Enterred OptionalAuthen->doPost()"); + + out.println("request.getRequestURI() = " + request.getRequestURI()); + out.println("request.getServletPath() = " + request.getServletPath()); + out.println("request.getPathInfo() = " + request.getPathInfo()); + out.println("request.getMethod() = " + request.getMethod()); + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("In OptionalAuthen->service()"); + PrintWriter out = response.getWriter(); + out.println("Enterred OptionalAuthen->service()"); + doPost(request, response); + } + +} diff --git a/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/WrapperServlet.java b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/WrapperServlet.java new file mode 100644 index 0000000..24d0380 --- /dev/null +++ b/tck/spi/servlet/src/main/java/ee/jakarta/tck/authentication/test/basic/servlet/WrapperServlet.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.servlet; + +import static java.util.logging.Level.INFO; + +import ee.jakarta.tck.authentication.test.common.logging.server.TSLogger; +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@DeclareRoles({ "Administrator", "Manager", "Employee" }) +@ServletSecurity(value = @HttpConstraint(rolesAllowed = { "Administrator" }), httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = "Administrator"), + @HttpMethodConstraint(value = "POST", rolesAllowed = "Administrator") }) +@WebServlet(name = "WrapperServlet", urlPatterns = { "/WrapperServlet" }) +public class WrapperServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private String providerConfigFileLocation; + private String vendorACFClass; + private String testMethod; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doTests(request, response); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doTests(request, response); + } + + public void doTests(HttpServletRequest request, HttpServletResponse response) { + debug("in WrapperServlet.doTests"); + + TSLogger logger = TSLogger.getTSLogger(); + + // get some common props + getPropsAndParams(request, response); + + // + // now send string out to logger so we can verify that the content + // gets processed BEFORE secureResponse. + debug("in WrapperServlet.doTests: calling logger.log()"); + logger.log(INFO, "WrapperServlet.doTests() content processed for requestURI"); + + if (testMethod.equals("testRequestWrapper")) { + testRequestWrapper(request, response); + } else if (testMethod.equals("testResponseWrapper")) { + testResponseWrapper(request, response); + } + + // Restore original (CTS) factory class + // note: this should be done in many of the calls but its a safety measure + // to ensure we are resetting things back to expected defaults + try { + CommonUtils.resetDefaultACF(); + } catch (Exception ex) { + debug("ACFTestServlet: error calling CommonUtils.resetDefaultACF(): " + ex.getMessage()); + ex.printStackTrace(); + } + } + + private void getPropsAndParams(HttpServletRequest req, HttpServletResponse response) { + // set provider config file + providerConfigFileLocation = System.getProperty("provider.configuration.file"); + debug("TS Provider ConfigFile = " + providerConfigFileLocation); + if (providerConfigFileLocation == null) { + debug("ERROR: getPropsAndParams(): providerConfigFileLocation = null"); + } else { + debug("getPropsAndParams(): providerConfigFileLocation = " + providerConfigFileLocation); + } + + // set testMethod + testMethod = req.getParameter("method.under.test"); + + // set vendor class + vendorACFClass = System.getProperty("vendor.authconfig.factory"); + if (vendorACFClass == null) { + debug("ERROR: getPropsAndParams(): vendorACFClass = null"); + } else { + debug("getPropsAndParams(): vendorACFClass = " + vendorACFClass); + } + + return; + } + + public void testRequestWrapper(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("WrapperServlet->testRequestWrapper()"); + out.println("isRequestWrapped = " + request.getAttribute("isRequestWrapped")); + out.flush(); + } catch (Exception ex) { + System.out.println("WrapperServlet->testRequestWrapper() failed"); + ex.printStackTrace(); + } + } + + public void testResponseWrapper(HttpServletRequest request, HttpServletResponse response) { + try { + PrintWriter out = response.getWriter(); + out.println("WrapperServlet->testResponseWrapper()"); + out.println("isResponseWrapped = " + response.getHeader("isResponseWrapped")); + out.flush(); + } catch (Exception ex) { + System.out.println("WrapperServlet->testResponseWrapper() failed"); + ex.printStackTrace(); + } + } + + public void debug(String str) { + System.out.println(str); + } + +} diff --git a/tck/spi/servlet/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/tck/spi/servlet/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 0000000..7a4388c --- /dev/null +++ b/tck/spi/servlet/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer \ No newline at end of file diff --git a/tck/spi/servlet/src/main/webapp/WEB-INF/web.xml b/tck/spi/servlet/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..180833e --- /dev/null +++ b/tck/spi/servlet/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + spi_servlet + + + BASIC + default + + + diff --git a/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java b/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java new file mode 100644 index 0000000..48c8b7e --- /dev/null +++ b/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletProfileSPITest.java @@ -0,0 +1,2185 @@ +package ee.jakarta.tck.authentication.test.basic; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.common.ArquillianBase; +import ee.jakarta.tck.authentication.test.common.logging.client.LogFileProcessor; +import jakarta.servlet.ServletContainerInitializer; +import org.htmlunit.WebResponse; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests a large number of SPI assertions from the Servlet profile. + * + * @author Arjan Tijms + * + */ +@RunWith(Arquillian.class) +public class ServletProfileSPITest extends ArquillianBase { + + // this must be the decoded context path corresponding to the web module + private String contextPath = "/" + JASPICData.SCP_CONTEXT_PATH; + private String servletPath = "ModTestServlet"; + private String acfServletPath = "ACFTestServlet"; + private String wrapperServletPath = "WrapperServlet"; + private String openToAllServletPath = "OpenToAllServlet"; + private String allAccessServletPath = openToAllServletPath; + private String staticPagePath = "client.html"; + private String noConstraintPath = "OptionalAuthen"; + + private String appContext = System.getProperty("logical.hostname.servlet") + " " + contextPath; + private String ACF_MSG_1 = "TSAuthConfigFactory.getConfigProvider " + "returned non-null provider for Layer : HttpServlet" + " and AppContext :" + appContext; + + @Deployment(testable = false) + public static Archive createDeployment() { + WebArchive archive = defaultWebArchive("spitests_servlet_web"); + archive.addAsServiceProvider(ServletContainerInitializer.class, AuthFactoryContainerInitializer.class); + + System.out.println(archive.toString(true)); + + return archive; + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckSecureRespForOptionalAuth + * + * @assertion_ids: JASPIC:SPEC:59; JASPIC:SPEC:304; JASPIC:JAVADOC:27; + * JASPIC:JAVADOC:10; JASPIC:JAVADOC:11; JASPIC:JAVADOC:28; + * JASPIC:JAVADOC:13; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify secureResponse was called. + * + * Description This method will make a URL post that has + * optional security Authen which means that we should NOT + * need to set any principals in the validateRequest methods + * nor the callbacks. The Runtime should allow this to proceed + * to call the secureResponse even though there is no + * principal values explicitly being set. Note: we need to + * specifically hardwire the particular context used in this + * test to NOT have any principals set on the server side. + * + */ + @Test + public void CheckSecureRespForOptionalAuth() { + getFromServerPath("OptionalAuthen"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue( + logProcessor.verifyLogContains( + "HttpServlet profile with servletName=/" + + "OptionalAuthen returning AuthStatus=AuthStatus.SUCCESS")); + + assertTrue( + logProcessor.verifyLogContains( + "secureResponse called for layer=HttpServlet for requestURI=" + + contextPath + "/OptionalAuthen")); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckSecureRespForMandatoryAuth + * + * @assertion_ids: JASPIC:SPEC:98; JASPIC:SPEC:304; JASPIC:JAVADOC:26; + * JASPIC:JAVADOC:31; JASPIC:JAVADOC:10; JASPIC:JAVADOC:9; + * JASPIC:SPEC:103; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify secureResponse and CallerPrincipalCallback are + * called. + * + * Description This method will make a URL post that has + * Mandatory security Authen which means that we need to set a + * principal objects in the validateRequest method + * clientSubject object (or in a callback). Either way, once + * we set a principal object in the clientSubject - The + * Runtime should proceed to call the secureResponse and + * should invoke the CallbackHandler passed to it by the + * runtime to handle a CallerPrincipalCallback using the + * clientSubject as argument to it. For this case, we want to + * ensure the following occur: - AuthStatus returns SUCCESS + * (we know this is occuriing otherwise we would not make it + * into secureResponse) - isMandatory() == true - + * secureResponse is called - CallerPrincipalCallback handler + * gets invoked. + * + * NOTE: We need our server side to check and ensure there are + * principal values being set for this particular context + * posting. + * + */ + @Test + public void CheckSecureRespForMandatoryAuth() { + String strHDR = "HttpServlet profile with servletName=/"; + String theContext = contextPath + "/" + JASPICData.AUTHSTAT_MAND_SUCCESS; + + String strMsg1 = strHDR + JASPICData.AUTHSTAT_MAND_SUCCESS + + " returning AuthStatus=AuthStatus.SUCCESS"; + String strMsg2 = "secureResponse called for layer=HttpServlet for requestURI=" + + theContext; + String strMsg3 = "In HttpServlet : ServerRuntime CallerPrincipalCallback"; + strMsg3 += " called for profile=HttpServlet for servletPath=" + theContext; + + getFromServerPath(JASPICData.AUTHSTAT_MAND_SUCCESS); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue( + logProcessor.verifyLogContains( + strMsg1, strMsg2, strMsg3)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: testSecRespCalledAfterSvcInvoc + * + * @assertion_ids: JASPIC:SPEC:108; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify secureResponse and CallerPrincipalCallback are + * called. + * + * Description This method will make a URL post that has + * Mandatory security Authen and it will verify that the + * secureResponse() method has NOT been called before the + * servlet/service invocation by checking for the existance of + * a cts proprietary request property that would only get set + * from in secureResponse. So if the property is found, we + * know a call to secureResponse was incorrectly made BEFORE + * the servlet/service invocation but if the request property + * is NOT found, then we can assume that secureResponse was + * NOT called before the servlet/service invocation. + * + */ + @Test + public void testSecRespCalledAfterSvcInvoc() { + String response = getFromServerPath( + JASPICData.AUTHSTAT_MAND_SUCCESS + "?" + "method.under.test=" + "testSecRespCalledAfterSvcInvoc"); + + assertTrue(response.contains("testSecRespCalledAfterSvcInvoc() passed")); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckNoAuthReturnsValidStatusCode + * + * @assertion_ids: JASPIC:SPEC:93 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Make servlet invocations and check the return status to + * verify proper server side handling. + * + * Description This method will make a request that is not + * authorized and then verify that the status code returned is + * not indicating the request was OK==200. If we get back a + * statuscode = 200 then there was a problem since we were + * trying to access a page that had mandatory authentication + * set AND we were trying to use an invalid user/pwd to + * perform that access - thus we should NOT get an okay status + * code returned since our bad username/pwd will not have + * perms. + * + */ + @Test + public void CheckNoAuthReturnsValidStatusCode() { + // invokeServlet will attempt to access the resource that does + // not have the proper role creds assigned to it + // thus we expect a return status code != 200 + WebResponse response = responseFromServerWithCredentials("AnotherMandatoryAuthen", "j2ee", "j2ee"); + + // if status of 200 was returned then there is a problem since + // we should have been forbidden from completing our request + // as our username/pwd change means we should not be authenticated + assertTrue(response.getStatusCode() != 200); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: VerifyRequestDispatchedProperly + * + * @assertion_ids: JASPIC:SPEC:317; JASPIC:JAVADOC:27; JASPIC:JAVADOC:28; + * + * + * @test_Strategy: 1. issue request that has mandatory auth and verify it gets + * authorized. + * + * 2. issue a request that is mandatory but NOT + * authorized (eg submit request with invalid user/pwd) and + * verify that secureResponse is still called by MPR. + * + * Description The request must be dispatched to the resource + * if the request was determined to be authorized; otherwise + * it must NOT be dispatched and the runtime must proceed to + * point (3) in the message processing model. + * + */ + @Test + public void VerifyRequestDispatchedProperly() { + // we should see that our request is properly dispatched + // we will assume that a proper response code indicates success + // as we are using correct creds + WebResponse response = responseFromServerWithCredentials( + JASPICData.AUTHSTAT_MAND_SUCCESS + + "?method.under.test=VerifyRequestDispatchedProperly", "j2ee", "j2ee"); + + assertTrue(response.getStatusCode() == 200); + + // invokeServlet will attempt to access the resource that + // does not have proper role creds assigned + // thus we expect a return status code != 200 + response = responseFromServerWithCredentials("AnotherMandatoryAuthen", "j2ee", "j2ee"); + + assertTrue(response.getStatusCode() != 200); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Now verify that the MPR went into the secureResponse + assertTrue( + logProcessor.verifyLogContains( + "secureResponse called for layer=HttpServlet for requestURI=" + + (getBase().getPath() + JASPICData.AUTHSTAT_MAND_SUCCESS))); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckValidateReqAuthException + * + * @assertion_ids: JASPIC:SPEC:58; JASPIC:SPEC:320; JASPIC:SPEC:94; + * JASPIC:JAVADOC:1; JASPIC:JAVADOC:28; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify secureResponse was not called for the given context + * and that an AuthException was called. + * + * Description This method will make a URL post using a + * context that should cause an AuthException to get thrown + * from the validateRequest. This should cause the + * secureResponse to NOT get called. + * + */ + @Test + public void CheckValidateReqAuthException() { + WebResponse response = responseFromServerWithCredentials( + JASPICData.AUTHSTAT_THROW_EX_ND, "j2ee", "j2ee"); + + assertTrue( + response.getStatusCode() == 403 || + response.getStatusCode() == 404 || + response.getStatusCode() == 500); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "HttpServlet profile with servletName=/" + + JASPICData.AUTHSTAT_THROW_EX_ND + " returning AuthStatus=AuthException")); + + // Also verify that no call to secureResponse was made + assertFalse( + logProcessor.verifyLogContains( + "secureResponse called for layer=HttpServlet for requestURI=" + + contextPath + "/" + JASPICData.AUTHSTAT_THROW_EX_ND)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckValidateReqAlwaysCalled + * + * @assertion_ids: JASPIC:SPEC:58; JASPIC:SPEC:320; JASPIC:SPEC:89; + * JASPIC:SPEC:94; JASPIC:JAVADOC:1; JASPIC:JAVADOC:28; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify secureResponse was not called for the given context + * and that an AuthException was called. + * + * Description This method will make mulitple posts using + * contexts w/ authN that is both required and non-required. + * We want to confirm that validateRequest is called + * independent of whether authN is required (i.e. including + * when isMandatory() would be false). Among other things, + * This is validating JASPIC spec section 3.8 which states + * "validateRequest must be called for all requests". + * + */ + @Test + public void CheckValidateReqAlwaysCalled() { + responseFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=CheckValidateReqAlwaysCalled", "j2ee", "j2ee"); + + responseFromServerWithCredentials( + "OptionalAuthen" + "?method.under.test=CheckValidateReqAlwaysCalled", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains( + "validateRequest() called for " + contextPath + "/ModTestServlet" + ", isMandatory() = true")); + + assertTrue(logProcessor.verifyLogContains( + "validateRequest() called for " + contextPath + "/OptionalAuthen" + ", isMandatory() = false")); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: AuthConfigFactoryGetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:84; + * JASPIC:JAVADOC:94; JASPIC:JAVADOC:79; JASPIC:JAVADOC:97; + * JASPIC:JAVADOC:77; JASPIC:JAVADOC:80; JASPIC:JAVADOC:84; + * JASPIC:JAVADOC:85; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify Whether AuthConfigFactory.getFactory is called and + * instantiated in the server. + * + * Description The AuthConfigFactory.getFactory method must be + * used during the container bootstrap processs. + * + */ + @Test + public void AuthConfigFactoryGetFactory() { + responseFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=AuthConfigFactoryGetFactory", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains( + "TSAuthConfigFactory.getFactory called Indirectly")); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: GetConfigProvider + * + * @assertion_ids: JASPIC:SPEC:8; JASPIC:SPEC:14; JASPIC:JAVADOC:79; + * JASPIC:JAVADOC:84; JASPIC:JAVADOC:85; JASPIC:JAVADOC:91 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify Whether AuthConfigFactory.getConfigProvider is + * called in the server. + * + * Description The runtime must invoke + * AuthConfigFactory.getConfigProvider to obtain the + * AuthConfigProvider. + * + */ + @Test + public void GetConfigProvider() { + responseFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=GetConfigProvider", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider called")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckAuthContextId + * + * @assertion_ids: JASPIC:SPEC:80 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify that the authentication context identifier used in + * the call to getAuthContext is equivalent to the value that + * is acquired in the call to getAuthContextID with the + * MessageInfo that is be used in the call to validateRequest. + * + * Description The authentication context identifier used in + * the call to getAuthContext must be equivalent to the value + * that would be acquired by calling getAuthContextID with the + * MessageInfo that will be used in the call to + * validateRequest. + * + */ + @Test + public void CheckAuthContextId() { + responseFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=CheckAuthContextId", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains( + "getAuthContextID() called for layer=HttpServlet shows"+ " AuthContextId=/ModTestServlet GET", + + "TSServerAuthConfig.getAuthContext: layer=HttpServlet" + + " : appContext=" + System.getProperty("logical.hostname.servlet") + " " + contextPath + + " operationId=/ModTestServlet GET")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: AuthConfigFactoryRegistration + * + * @assertion_ids: JASPIC:SPEC:80; + * + * @test_Strategy: this was originally in register directory but was moved + * here and the code pulled out and centralized into + * ACFTestServlet. + * + */ + @Test + public void AuthConfigFactoryRegistration() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=AuthConfigFactoryRegistration", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->AuthConfigFactoryRegistration() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFInMemoryNotifyOnUnReg + * + * @assertion_ids: JASPIC:SPEC:336; JASPIC:SPEC:337; JASPIC:SPEC:338; + * JASPIC:SPEC:339; JASPIC:SPEC:342; JASPIC:SPEC:344; + * + * @test_Strategy: this was originally in register directory but was moved + * here and the code pulled out and centralized into + * ACFTestServlet. + * + */ + @Test + public void ACFInMemoryNotifyOnUnReg() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFInMemoryNotifyOnUnReg", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFInMemoryNotifyOnUnReg() passed")); + + } + + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFPersistentNotifyOnUnReg + * + * @assertion_ids: JASPIC:SPEC:336; JASPIC:SPEC:337; JASPIC:SPEC:338; + * JASPIC:SPEC:339; JASPIC:SPEC:341; JASPIC:SPEC:344; + * + * @test_Strategy: this was originally in register directory but was moved + * here and the code pulled out and centralized into + * ACFTestServlet. + * + */ + @Test + public void ACFPersistentNotifyOnUnReg() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFPersistentNotifyOnUnReg", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFPersistentNotifyOnUnReg() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFInMemoryPrecedenceRules + * + * @assertion_ids: JASPIC:SPEC:328; JASPIC:SPEC:337; JASPIC:SPEC:338; + * JASPIC:SPEC:338; JASPIC:SPEC:339; JASPIC:SPEC:344; + * + * @test_Strategy: this was originally in register directory but was moved + * here and the code pulled out and centralized into + * ACFTestServlet. + * + */ + @Test + public void ACFInMemoryPrecedenceRules() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFInMemoryPrecedenceRules", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFInMemoryPrecedenceRules() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFPersistentPrecedenceRules + * + * @assertion_ids: JASPIC:SPEC:328; JASPIC:SPEC:337; JASPIC:SPEC:338; + * JASPIC:SPEC:338; JASPIC:SPEC:339; JASPIC:SPEC:344; + * + * @test_Strategy: this was originally in register directory but was moved + * here and the code pulled out and centralized into + * ACFTestServlet. + * + */ + @Test + public void ACFPersistentPrecedenceRules() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFPersistentPrecedenceRules", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFPersistentPrecedenceRules() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFGetRegistrationContext + * + * @assertion_ids: JASPIC:JAVADOC:81; + * + * @test_Strategy: This is testing that acf.getRegistrationContext(string) + * returns NULL for an unrecognized string. (this requirement + * described in javadoc for api) + */ + @Test + public void ACFGetRegistrationContext() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFGetRegistrationContext", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFGetRegistrationContext() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFGetRegistrationIDs + * + * @assertion_ids: JASPIC:JAVADOC:82; + * + * @test_Strategy: This is testing that acf.getRegistrationIDs(acp) NEVER + * returns null hint: this must return empty array even if + * unrecognized acp. (this requirement described in javadoc + * for api) + * + */ + @Test + public void ACFGetRegistrationIDs() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFGetRegistrationIDs", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFGetRegistrationIDs() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFRemoveRegistration + * + * @assertion_ids: JASPIC:JAVADOC:86; + * + * @test_Strategy: This is testing that acf.removeRegistration(arg) will + * return FALSE when invalid arg supplied. (this requirement + * described in javadoc for api) + * + */ + @Test + public void ACFRemoveRegistration() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFRemoveRegistration", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFRemoveRegistration() passed")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFDetachListener + * + * @assertion_ids: JASPIC:JAVADOC:78; + * + * @test_Strategy: This is testing that acf.detachListener(...) will return + * non-null when unregistered listener supplied. (this + * requirement described in javadoc for api) + * + */ + @Test + public void ACFDetachListener() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFDetachListener", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFDetachListener() passed")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFGetFactory + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:335; JASPIC:SPEC:7; + * + * @test_Strategy: This s mainly concerned with testing the runtimes handling + * of ACF as follows: - get current (CTS) ACF - switch to use + * different (CTS) ACF - verify calls to ACF use the + * newer/expected ACF - restore original ACF + */ + @Test + public void ACFGetFactory() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFGetFactory", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFGetFactory() passed")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testACFComesFromSecFile + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:330; + * + * @test_Strategy: This is calling a method on the server(actually servlet) + * side that will invoke getFactory() to verify a non-null + * facotry instance is returned. It will also verify that the + * authconfigprovider.factory security property is properly + * set/used. + * + */ + @Test + public void testACFComesFromSecFile() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=testACFComesFromSecFile", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->testACFComesFromSecFile() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFPersistentRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:331; JASPIC:SPEC:332; + * JASPIC:SPEC:340; JASPIC:SPEC:341; + * + * @test_Strategy: This will make a server (actually servlet) side method call + * that will do the following: - load vendors ACF - + * (persistent) register of CTS ACP's in the vendors ACF - get + * list of vendors registered provider ID's - register another + * persistent ACP (this is standalone ACP profile) - verify + * another regId was added for standalone ACP - try to + * re-register (persistently) standalone provider - verify 2nd + * attempt at added standalone provider REPLACED the first but + * it should NOT have added another nor failed. - also confirm + * that regID for standalone ACP stayed the same after 1st and + * 2nd attempt to register standalone ACP - verify that the + * 2nd re-registering of ACP replaced the ACP AND the + * description. - unregister standalone ACP and setFactory + * back to CTS default + * + */ + @Test + public void ACFPersistentRegisterOnlyOneACP() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFPersistentRegisterOnlyOneACP", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFPersistentRegisterOnlyOneACP() passed")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFInMemoryRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:334; JASPIC:SPEC:342; JASPIC:SPEC:343; + * + * @test_Strategy: This will make a server (actually servlet) side method call + * that will do the following: - load vendors ACF - + * (persistent) register of CTS ACP's in the vendors ACF - get + * list of vendors registered provider ID's - register + * (in-memory) ACP (this is standalone ACP profile) - verify + * another regId was added for standalone ACP - try to + * re-register (in-memory) standalone provider - verify 2nd + * attempt at added standalone provider REPLACED the first but + * it should NOT have added another nor failed. - also confirm + * that regID for standalone ACP stayed the same after 1st and + * 2nd attempt to register standalone ACP - verify that the + * 2nd re-registering of ACP replaced the ACP AND the + * description. - unregister standalone ACP and setFactory + * back to CTS default + * + */ + @Test + public void ACFInMemoryRegisterOnlyOneACP() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFInMemoryRegisterOnlyOneACP", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFInMemoryRegisterOnlyOneACP() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: ACFUnregisterACP + * + * @assertion_ids: JASPIC:SPEC:344; + * + * @test_Strategy: This will make a server (actually servlet) side method call + * that will do the following: - load vendors ACF - + * (persistent) register of CTS ACP's in the vendors ACF - get + * list of vendors registered provider ID's - register + * (in-memory) ACP (this is standalone ACP profile) - verify + * another regId was added for standalone ACP - unregister the + * in-memory ACP we just registered - verify + * removeRegistration() method call returned proper boolean - + * verify expected # of registry eentries - verify 2nd call to + * removeRegistration() with regId that was previously removed + * and should expect return val of false + * + */ + @Test + public void ACFUnregisterACP() { + String response = readFromServerWithCredentials( + acfServletPath + "?method.under.test=ACFUnregisterACP", "j2ee", "j2ee"); + + assertTrue(response.contains("ACFTestServlet->ACFUnregisterACP() passed")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckACFVerifyPersistence + * + * @assertion_ids: JASPIC:SPEC:75; JASPIC:SPEC:335; + * + * @test_Strategy: This tests the (persistent) registration of CTS ACP's + * within the the vendors ACF. + * + */ + @Test + public void CheckACFVerifyPersistence() { + String response = readFromServerWithCredentials( + acfServletPath, "j2ee", "j2ee"); + + assertTrue(response.contains("AuthConfigFactoryVerifyPersistence() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckRegistrationContextId + * + * @assertion_ids: JASPIC:SPEC:77; + * + * @test_Strategy: This registers CTS ACP's within vendors ACF and then + * verifies the RegistrationContext info can be obtained from + * the vendors ACF. + * + */ + @Test + public void CheckRegistrationContextId() { + String response = readFromServerWithCredentials( + acfServletPath, "j2ee", "j2ee"); + + assertTrue(response.contains("getRegistrationContextId() TSProviders registration passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testRequestWrapper + * + * @assertion_ids: JASPIC:SPEC:108; JASPIC:SPEC:311; + * + * @test_Strategy: + * + * From JASPIC Spec section 3.8.3.5: When a ServerAuthModule + * returns a wrapped message in MessageInfo, or unwraps a + * message in MessageInfo, the message processing runtime must + * ensure that the HttpServletRequest and HttpServletResponse + * objects established by the ServerAuthModule are used in + * downstream processing. + * + * This test uses a Server Authentication Module(SAM) that + * wraps and unwraps the HttpRequest as specified by the + * JASPIC spec and expects the runtime to handle the Wrapped + * HttpRequest. + * + */ + @Test + public void testRequestWrapper() { + String response = readFromServerWithCredentials( + wrapperServletPath + "?method.under.test=testRequestWrapper", "j2ee", "j2ee"); + + assertTrue(response.contains("isRequestWrapped = true")); + assertFalse(response.contains("Incorrect request type")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testResponseWrapper + * + * @assertion_ids: JASPIC:SPEC:108; JASPIC:SPEC:311; + * + * @test_Strategy: From JASPIC Spec section 3.8.3.5: When a ServerAuthModule + * returns a wrapped message in MessageInfo, or unwraps a + * message in MessageInfo, the message processing runtime must + * ensure that the HttpServletRequest and HttpServletResponse + * objects established by the ServerAuthModule are used in + * downstream processing. + * + * This test uses a Server Authentication Module(SAM) that + * wraps and unwraps the HttpResponse as specified by the + * JASPIC spec and expects the runtime to handle the Wrapped + * HttpResponse. + */ + @Test + public void testResponseWrapper() { + String response = readFromServerWithCredentials( + wrapperServletPath + "?method.under.test=testResponseWrapper", "j2ee", "j2ee"); + + assertTrue(response.contains("isResponseWrapped = true")); + assertFalse(response.contains("Incorrect request type")); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:72; JASPIC:SPEC:103; JASPIC:JAVADOC:27; JASPIC:JAVADOC:40; JASPIC:JAVADOC:46; + * JASPIC:JAVADOC:31; JASPIC:JAVADOC:32; JASPIC:JAVADOC:33; JASPIC:JAVADOC:34; JASPIC:JAVADOC:38; JASPIC:JAVADOC:39; + * JASPIC:JAVADOC:41; JASPIC:JAVADOC:42; JASPIC:JAVADOC:43; JASPIC:JAVADOC:44; JASPIC:JAVADOC:35; JASPIC:JAVADOC:36; + * JASPIC:JAVADOC:51; JASPIC:JAVADOC:49; JASPIC:JAVADOC:54; JASPIC:JAVADOC:65; JASPIC:JAVADOC:63; JASPIC:JAVADOC:68; + * JASPIC:JAVADOC:69; JASPIC:JAVADOC:71; JASPIC:JAVADOC:28; JASPIC:JAVADOC:107; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. read the server side log to verify Whether the following callbackHandlers are supported: + * CallerPrincipalCallback, GroupPrincipalCallback, and PasswordValidationCallback. + * + * Description The CallbackHandler passed to ServerAuthModule.initialize must support the following callbacks: + * CallerPrincipalCallback, GroupPrincipalCallback, and PasswordValidationCallback. + * + */ + @Test + public void CheckCallbackSupport() { + String strMsg1 = "In HttpServlet : ServerRuntime CallbackHandler supports CallerPrincipalCallback"; + String strMsg2 = "In HttpServlet : ServerRuntime CallbackHandler supports GroupPrincipalCallback"; + String strMsg3 = "In HttpServlet : ServerRuntime CallbackHandler supports PasswordValidationCallback"; + + // by invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see which CBH's are supported. + responseFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=CheckCallbackSupport", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains(strMsg1, strMsg2, strMsg3)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testGPCIsUserInRole + * + * @assertion_ids: JASPIC:SPEC:90; + * @test_Strategy: The ModTestServlet is called which forces our SAM to invoke + * callbacks - including the GPC. This test expects and checks + * that the GPC was invoked with non-null principal and that + * auth was mandatory and that upon return from + * validateRequest, there are values set in + * HttpServletRequest.isUserInRole() + * + * Description This is checking that the group callbacks work + * for case of being called with CPC when auth is required and + * the check that the validateRequest ensures there was a + * return value set for calls to + * HttpServletRequest.getRemoteUser() + * + */ + @Test + public void testGPCIsUserInRole() { + String strMsg1 = "In HttpServlet : ServerRuntime GroupPrincipalCallbackSupport():"; + strMsg1 += " successfully called callbackHandler.handle(callbacks)"; + strMsg1 += " for servlet: /ModTestServlet"; + strMsg1 += " with isServletAuthMandatory = " + true; + + // by invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see that GPC is supported and + // that the IsUserInRole() is properly populated. + String str = readFromServerWithCredentials("ModTestServlet" + "?method.under.test=testGPCIsUserInRole", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + String msg = + "testGPCIsUserInRole did not have proper credential values returned in " + + "ModTestServlet" + ". this could be due to CBH's not properly doing authentication."; + + assertTrue(msg, str.contains("ModTestServlet->testGPCIsUserInRole() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testGPCGetUserPrincipal + * + * @assertion_ids: JASPIC:SPEC:90; + * @test_Strategy: The ModTestServlet is called which forces our SAM to invoke + * callbacks - including the GPC. This test expects and checks + * that the GPC was invoked with non-null principal and that + * auth was mandatory and that upon return from + * validateRequest, there are values set in + * HttpServletRequest.getUserPrincipal() + * + * Description This is checking that the group callbacks work + * for case of being called with CPC when auth is required and + * the check that the validateRequest ensures there was a + * return value set for calls to + * HttpServletRequest.getUserPrincipal() + * + */ + @Test + public void testGPCGetUserPrincipal() { + + String strMsg1 = "In HttpServlet : ServerRuntime GroupPrincipalCallbackSupport():"; + strMsg1 += " successfully called callbackHandler.handle(callbacks)"; + strMsg1 += " for servlet: /ModTestServlet"; + strMsg1 += " with isServletAuthMandatory = " + true; + + // by invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see that GPC is supported and + // that the GetUserPrincipal() is properly populated. + String str = readFromServerWithCredentials("ModTestServlet" + "?method.under.test=testGPCGetUserPrincipal", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + String msg = "testGPCGetUserPrincipal did not have proper credential values returned in "; + msg += "ModTestServlet" + ". this could be due to CBH's not properly doing authentication."; + + assertTrue(msg, str.contains("ModTestServlet->testGPCGetUserPrincipal() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testGPCGetAuthType + * + * @assertion_ids: JASPIC:SPEC:90; + * @test_Strategy: The ModTestServlet is called which forces our SAM to invoke + * callbacks - including the GPC. This test expects and checks + * that the GPC was invoked with non-null principal and that + * auth was mandatory and that upon return from + * validateRequest, there are values set in + * HttpServletRequest.getAuthType() + * + * Description This is checking that the group callbacks work + * for case of being called with CPC when auth is required and + * the check that the validateRequest ensures there was a + * return value set for calls to + * HttpServletRequest.getAuthType() + * + */ + @Test + public void testGPCGetAuthType() { + + String strMsg1 = "In HttpServlet : ServerRuntime GroupPrincipalCallbackSupport():"; + strMsg1 += " successfully called callbackHandler.handle(callbacks)"; + strMsg1 += " for servlet: /ModTestServlet"; + strMsg1 += " with isServletAuthMandatory = " + true; + + // by invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see that GPC is supported and + // that the GetAuthType() is properly populated. + String str = readFromServerWithCredentials("ModTestServlet" + "?method.under.test=testGPCGetAuthType", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + String msg = "testGPCGetAuthType did not have proper credential values returned in "; + msg += "ModTestServlet" + ". this could be due to CBH's not properly doing authentication."; + + assertTrue(msg, str.contains("ModTestServlet->testGPCGetAuthType() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckAuthenInValidateRequest + * + * @assertion_ids: JASPIC:SPEC:90; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify if the validateRequest() was called under the + * scenario of already being logged in. If we are already + * logged in, then there are (3) HttpServletRequest methods + * that must return non-null values:` - getAuthType() - + * getRemoteUser() - getUserPrincipal() + * + * Description The TSSVLog.txt file looking for string that + * indicates we found a scenario with only some of the + * forementioned methods returned non-null. If a user is + * authenticated, they should return non-null values. If the + * user is NON authenticated, these methods should return + * null. + * + */ + @Test + public void CheckAuthenInValidateRequest() { + String strMsg1 = "validateRequest(): ERROR - invalid authen scenario."; + String tempArgs[] = { strMsg1 }; + + // by invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see which CBH's are supported. + readFromServerWithCredentials("ModTestServlet" + "?method.under.test=CheckAuthenInValidateRequest", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertFalse(logProcessor.verifyLogContains(tempArgs)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testAuthenResultsOnHttpServlet + * + * @assertion_ids: JASPIC:SPEC:90; JASPIC:SPEC:322; + * + * @test_Strategy: When authentication is mandatory, after AuthStatus.SUCCESS + * is returned from validateRequest, we should be able to + * verify that HttpServletRequest has non-null values set for + * the following (3) method calls: request.getAuthType() + * request.getRemoteUser() request.getUserPrincipal() + * + * This is based on javadoc for HttpServletRequest as well as + * on jsr-196 v1.1 spec section 3.8.1.3 and 3.8.4. + * + */ + @Test + public void testAuthenResultsOnHttpServlet() { + String str = readFromServerWithCredentials("ModTestServlet" + "?method.under.test=testAuthenResultsOnHttpServlet", "j2ee", "j2ee"); + + assertTrue(str.contains("ModTestServlet->testAuthenResultsOnHttpServlet() passed")); + } + + + + /** + * @keywords: jaspic_servlet + * + * @testName: testRemoteUserCorrespondsToPrin + * + * @assertion_ids: JASPIC:SPEC:321; + * + * @test_Strategy: When authentication is mandatory, after AuthStatus.SUCCESS + * is returned from validateRequest, we should be able to + * verify that (a) HttpServletRequest has non-null values set + * for the following methods: request.getRemoteUser() and + * request.getUserPrincipal() (b) that getRemoteUser() and + * getUserPrincipal().getName() are equal. + * + * This is based on javadoc for HttpServletRequest as well as + * on jsr-196 v1.1 spec section 3.8.4. Specifically, this is + * validating that: "In both cases, the HttpServletRequest + * must be modified as necessary to ensure that the Principal + * returned by getUserPrincipal and the String returned by + * getRemoteUser correspond, ..." Using the above spec + * reference COMBINED with the following Servlet spec + * reference, we see that: + * + * (Using Servlet spec v3.1, section 13.3 - it states: "The + * getRemoteUser method returns the name of the remote user" + * AND "Calling the getName() method on the Principal returned + * by getUserPrincipal() returns the name of the remote user." + * We can use these two Servlet spec references combined with + * our JASPIC reference to validate that principal.getName() + * equals (i.e. "corresponds") to the same value returned by + * getRemoteUser(). + * + */ + @Test + public void testRemoteUserCorrespondsToPrin() { + String str = readFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=testRemoteUserCorrespondsToPrin", "j2ee", "j2ee"); + + assertTrue(str.contains("ModTestServlet->testRemoteUserCorrespondsToPrin() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testAuthenIsUserInRole + * + * @assertion_ids: JASPIC:SPEC:321; + * + * @test_Strategy: When authentication is mandatory, after AuthStatus.SUCCESS + * is returned from validateRequest, we should be able to + * verify that HttpServletRequest has non-null values set for + * the following method call: request.isUserInRole() + * + * This is based on javadoc for HttpServletRequest as well as + * on jsr-196 v1.1 spec section 3.8.1.3 and 3.8.4. There is no + * direct spec reference, but it is a scenario that can be + * inferred. + */ + @Test + public void testAuthenIsUserInRole() { + String str = readFromServerWithCredentials( + "ModTestServlet" + "?method.under.test=testAuthenIsUserInRole", "j2ee", "j2ee"); + + assertTrue(str.contains("ModTestServlet->testAuthenIsUserInRole() passed")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testAuthenAfterLogout + * + * @assertion_ids: JASPIC:SPEC:98; + * + * @test_Strategy: When authentication is mandatory, after AuthStatus.SUCCESS + * is returned from validateRequest, we should be able to + * verify that we can properly login and invoke callback + * handlers. + * + * This test ensures that we can authN when we are not already + * pre-logged in. This will call a servlet that is open to + * all, (thus no security constraints) and it will invoke the + * servlet logout() command. Then it will make a call to a + * servlet which requires mandatory authentication. The + * premise being that authentication does occur - especially + * if not pre-logged in. + * + * Note the spec does not state that you can not be pre-logged + * in but it is saying that authentication has to be able to + * succeed in a particular manner. (see spec section 3.8.3.1) + * + */ + @Test + public void testAuthenAfterLogout() { + // call a servlet that is open to all and requires NO authN to access + // validate we were not pre-authenticated and if so, force logout + String str = readFromServerWithCredentials(allAccessServletPath + "?method.under.test=TestAuthenAfterLogout", "j2ee", "j2ee"); + + // now make a call to a servlet that requires mandatory authN and + // make sure we still successfully support callbacks + str = readFromServerWithCredentials(servletPath + "?method.under.test=testAuthenAfterLogout", "j2ee", "j2ee"); + + assertTrue(str.contains("ModTestServlet->testAuthenAfterLogout() passed")); + + // Next make sure we see our CPC being properly called. + String strMsg2 = "In HttpServlet : ServerRuntime CallerPrincipalCallback"; + strMsg2 += " called for profile=HttpServlet for servletPath=" + contextPath + "/" + servletPath; + String strMsg3 = "Validated we are not prelogged in for OpenToAllServlet"; + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strMsg2, strMsg3)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testGPCWithNoRequiredAuth + * + * @assertion_ids: JASPIC:SPEC:90; + * @test_Strategy: This is checking that the group callbacks work for case of + * being called with CPC when no auth is required. + * + * Description This ultmately calls + * ServerCallbackSupport.GroupPrincipalCallbackSupport() with + * no required creds. + */ + @Test + public void testGPCWithNoRequiredAuth() { + + // By invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see which CBH's are supported. + readFromServerWithCredentials(allAccessServletPath + "?method.under.test=testGPCWithNoRequiredAuth", "j2ee", "j2ee"); + + String strMsg1 = "In HttpServlet : ServerRuntime GroupPrincipalCallbackSupport():"; + strMsg1 += " successfully called callbackHandler.handle(callbacks)"; + strMsg1 += " for servlet: " + "/" + openToAllServletPath; + strMsg1 += " with isServletAuthMandatory = " + false; + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: testGPCGetRemoteUser + * + * @assertion_ids: JASPIC:SPEC:90; + * @test_Strategy: The ModTestServlet is called which forces our SAM to invoke + * callbacks - including the GPC. This test expects and checks + * that the GPC was invoked with non-null principal and that + * auth was mandatory and that upon return from + * validateRequest, there are values set in + * HttpServletRequest.getRemoteUser() + * + * Description This is checking that the group callbacks work + * for case of being called with CPC when auth is required and + * the check that the validateRequest ensures there was a + * return value set for calls to + * HttpServletRequest.getRemoteUser() + * + */ + @Test + public void testGPCGetRemoteUser() { + + String strChkServlet = "ModTestServlet->testGPCGetRemoteUser() passed"; + + String strMsg1 = "In HttpServlet : ServerRuntime GroupPrincipalCallbackSupport():"; + strMsg1 += " successfully called callbackHandler.handle(callbacks)"; + strMsg1 += " for servlet: /ModTestServlet"; + strMsg1 += " with isServletAuthMandatory = " + true; + + // By invoking this servlet, we are causing code to access our + // servlet profile TSServerAuthModule.validateRequest() method + // which will perform checks to see that GPC is supported and + // that the GetRemoteUser() is properly populated. + String str = readFromServerWithCredentials(servletPath + "?method.under.test=testGPCGetRemoteUser", "j2ee", "j2ee"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // First, lets make sure CBH's were actually called + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + // Next, lets make sure CBH's set principal values as expected + String msg = "testGPCGetRemoteUser did not have proper credential values returned in "; + msg += servletPath + ". this could be due to CBH's not properly doing authentication."; + assertTrue(msg, str.contains(strChkServlet)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: verifyRuntimeCallOrder + * + * @assertion_ids: JASPIC:SPEC:317; JASPIC:SPEC:304; + * + * @test_Strategy: This test verifies that the runtime is invoking the proper + * calling order of: validateRequest, process request, + * secureResponse. + * + * Details After AuthStatus.SUCCESS is returned from + * validateRequest, we need to verify that the request was + * dispatched, and then that the secureResponse() was called - + * in that order. We do this by verifying that the messages + * (i.e. strMsg1, strMsg2, and strMsg3) were called in the + * specific order. The verifyLogContains() method checks these + * strings exist within the TSSVLog.txt file AND that they + * exist within the log file in order that matches the way + * they are declared via "theArgs[]". + * + */ + @Test + public void verifyRuntimeCallOrder() { + // Strings that MUST exist within the authentication-trace-log.xml file + String strMsg1 = "TSServerAuthContext.validateRequest called for layer=HttpServlet for requestURI=" + contextPath + "/" + wrapperServletPath; + String strMsg2 = "WrapperServlet.doTests() content processed for requestURI"; + String strMsg3 = "secureResponse called for layer=HttpServlet for requestURI=" + contextPath + "/" + wrapperServletPath; + + invokeServletAndGetResponse(wrapperServletPath, "POST", "testRequestWrapper"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(true, strMsg1, strMsg2, strMsg3)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifyClientSubjects + * + * @assertion_ids: JASPIC:SPEC:96; JASPIC:SPEC:52; JASPIC:JAVADOC:28; + * JASPIC:JAVADOC:106; JASPIC:SPEC:23; JASPIC:SPEC:19; + * JASPIC:SPEC:51; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. read the server side log to + * verify whether the ClientSubject passed to validateRequest + * was null or not. + * + * Description According to spec section 3.8.2: A new + * clientSubject must be instantiated and passed in the call + * to validateRequest. + * + */ + @Test + public void VerifyClientSubjects() { + String strMsg1 = "HttpServlet profile: "; + strMsg1 += "TSServerAuthContext.validateRequest called with non-null client Subject"; + + invokeServlet(servletPath, "POST", "VerifyClientSubjects"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // First, verify we have a non-null clientSubject + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + // Now verify we have no occurances of null clientSubjects + checkForInvalidClientSubjects(logProcessor); + + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckMessageInfo + * + * @assertion_ids: JASPIC:SPEC:95; JASPIC:JAVADOC:28; JASPIC:JAVADOC:10; JASPIC:JAVADOC:11; JASPIC:SPEC:23; + * JASPIC:SPEC:19; JASPIC:SPEC:69; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. read the server side log to verify Whether the messageInfo passed to validateRequest() + * contains proper values for getRequestMessage() and getResponseMessage(). + * + * Description The MessageInfo argument used in any call made by the message processing runtime to validateRequest must + * have been initialized such that the non-null objects returned by the getRequestMessage and getResponseMessage methods + * of the MessageInfo are an instanceof HttpServletRequest and HttpServletResponse. + * + */ + @Test + public void CheckMessageInfo() { + String strMsg1 = "validateRequest: MessageInfo.getRequestMessage() is of type "; + String strMsg2 = "validateRequest: MessageInfo.getResponseMessage() is of type "; + + // This should work for servlets and static pages + // invoking a static should cause the validateRequest to be called + invokeServlet(staticPagePath, "POST", "CheckMessageInfo"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue( + logProcessor.verifyLogContains( + strMsg1 + "jakarta.servlet.http.HttpServletRequest", + strMsg2 + "jakarta.servlet.http.HttpServletResponse" )); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifyReqPolicy + * + * @assertion_ids: JASPIC:SPEC:87; JASPIC:JAVADOC:107; JASPIC:JAVADOC:17; JASPIC:JAVADOC:14; JASPIC:JAVADOC:21 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. read the server side log to verify we have more than 1 target policy AND that + * ProtectionPolicy.getID() returns a proper value. + * + * + * Description Calling getTargetPolicies on the request MessagePolicy must return an array containing at least one + * TargetPolicy whose ProtectionPolicy will be interpreted by the modules of the context to mean that the source of the + * corresponding targets within the message is to be authenticated. To that end, calling the getID method on the + * ProtectionPolicy must return one of the following values: ProtectionPolicy.AUTHENTICATE_SENDER, + * ProtectionPolicy.AUTHENTICATE_CONTENT + * + */ + @Test + public void VerifyReqPolicy() { + invokeServlet(servletPath, "POST", "VerifyReqPolicy"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // First make sure we have an array with more than 1 TargetPolicy + checkForInvalidReqPolicy(logProcessor); + + // Next, make sure ProtectionPolicy.getID() returns valid value + checkForInvalidProtectionPolicyID(logProcessor); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: checkSACValidateRequestWithVaryingAccess + * + * @assertion_ids: JASPIC:SPEC:89; JASPIC:JAVADOC:28 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. read the server side log to verify ServerAuthContext.validateRequest() is called for our + * servlet container even when no access will be granted. + * + * Description This will test that the message processing runtime must call validateRequest independent of whether or + * not access to the resource would have been permitted prior to the call to validateRequest. Note: This particular + * assertion actually only applies to the case of acceptable connections that have been negotiated and will not be + * required for cases where the WebUserDataPermission objects allow a container to determine when to reject a request + * befor redirection of that request would ultimately be rejected as a result of an excluding auth-constraint. (an + * excluding auth-constraint is an auth-constraint that has no roles. (see JSR-115 spec, section 3.1.3.1, footnote 1) + */ + @Test + public void checkSACValidateRequestWithVaryingAccess() { + String HDR = "TSServerAuthContext.validateRequest called for layer=HttpServlet"; + String FTR = " AuthStatus=AuthStatus.SUCCESS"; + String strMsg1 = HDR + " for requestURI=" + contextPath + "/" + noConstraintPath + FTR; + String strMsg2 = HDR + " for requestURI=" + contextPath + "/" + servletPath + FTR; + + // Invoking a servlet should cause the validateRequest to be called + // and here we are invoking a url that has no auth-constraints + invokeServlet(noConstraintPath, "POST", "checkSACValidateRequestWithVaryingAccess"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strMsg1)); + + // Invoking a servlet should cause the validateRequest to be called + // and here we are invoking a url that has auth-constraints + invokeServlet(servletPath, "POST", "checkSACValidateRequestWithVaryingAccess"); + + assertTrue(logProcessor.verifyLogContains(strMsg2)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifyMessageInfoObjects + * + * @assertion_ids: JASPIC:SPEC:60; JASPIC:JAVADOC:27; JASPIC:JAVADOC:28; JASPIC:SPEC:305; + * + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Invoke a servlet to cause a validateRequest invocation, (make sure it is a servlet that has Authen Permissions + * enabled so that we can return AuthStatus.SUCCESS from validateRequest() so that we can be sure to enter into + * secureResponse(). Once in secureResponse, we want to validate the MessageInfo object is the same as the MessageInfo + * object passed into the validateRequest() + * + * Description Verify that the MessageInfo object passed into the validateRequest() method is the same MessageInfo + * object that is passed into secureResponse(). + * + */ + @Test + public void VerifyMessageInfoObjects() { + String strMsg1 = "MessageInfo object from secureRequest matches "; + strMsg1 += "the messageInfo object from validateRequest"; + + // Invoking this servlet should cause the validateRequest and + // the secureResponse to be called. + invokeServlet(JASPICData.AUTHSTAT_MAND_SUCCESS, "POST", "VerifyMessageInfoObjects"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifyServiceSubjects + * + * @assertion_ids: JASPIC:SPEC:53; JASPIC:SPEC:61; JASPIC:SPEC:314; JASPIC:SPEC:313; JASPIC:JAVADOC:27; + * JASPIC:JAVADOC:28; JASPIC:SPEC:24 + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Invoke a servlet to cause a validateRequest invocation, then read the server side log to + * verify we have no valid service subject messages. + * + * Description Verify that If a non-null Subject was used to acquire the ServerAuthContext, the same Subject must be + * passed as the serviceSubject in this call (ie to Point 2 in MPR model). + * + */ + @Test + public void VerifyServiceSubjects() { + + // Ideally we'd like to find a platform independant way to + // also verify that we have valid (non-null) serviceSubjects + // but I don't believe this is possible so for now, we will + // be content to verify no invalid serviceSubjects were found + + // Invoking a servlet should cause the validateRequest to be called + // but does not guarantee a non-null serviceSubject + invokeServlet(servletPath, "POST", "VerifyServiceSubjects"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Currently we verify that there were no invalid serviceSubject + checkForInvalidServiceSubjects(logProcessor); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifySAContextVerifyReqIsCalled + * + * @assertion_ids: JASPIC:SPEC:88; JASPIC:SPEC:50; JASPIC:JAVADOC:28 + * + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify ServerAuthContext.validateRequest() is called for our + * servlet container. + * + * Description The inbound processing of requests by a servlet container corresponds to point (2) in the message + * processing model. At point (2) in the message processing model, the runtime must call validateRequest on the + * ServerAuthContext. + * + */ + @Test + public void VerifySAContextVerifyReqIsCalled() { + // Invoking a servlet should cause the validateRequest to be called + invokeServlet(servletPath, "POST", "VerifySAContextVerifyReqIsCalled"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains("TSServerAuthContext.validateRequest called for layer=HttpServlet")); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: CheckMsgInfoKey + * + * @assertion_ids: JASPIC:SPEC:306; JASPIC:SPEC:300; JASPIC:JAVADOC:73; JASPIC:SPEC:92; JASPIC:SPEC:24; JASPIC:SPEC:103; + * + * + * @test_Strategy: + * 1. invoke servlet that requires authentication + * 2. verify that the MesageInfo object passed into getAuthContextID, validateRequest, and secureResponse + * all had valid key-value pairs. + * + * Description: This profile requires that the message processing runtime conditionally establish the following + * key-value pair within the Map of the MessageInfo object passed in the calls to getAuthContextID, validateRequest, and + * secureResponse. (key) jakarta.security.auth.message.MessagePolicy.isMandatory (val) Any non-null String value, s, for + * which Boolean.valueOf(s).booleanValue() == true. The MessageInfo map must contain this key and its associated value, + * if and only if authentication is required to perform the resource access corresponding to the HttpServletRequest to + * which the ServerAuthContext will be applied. + * + * If we are Jakarta Authorization compatible, there is an additional test we must make. The key=jakarta.security.jacc.PolicyContext + * better exist in the Properties arg passed into getAuthContext(). + * + */ + @Test + public void CheckMsgInfoKey() { + String CMN_HDR = "dumpServletProfileKeys() called with attrs: layer=HttpServlet"; + CMN_HDR += " servletName=/ModTestServlet callerMethod="; + + String CMN_TAIL = " key=jakarta.security.auth.message.MessagePolicy.isMandatory"; + CMN_TAIL += " value=Valid"; + + String strAuthCtxt = CMN_HDR + "getAuthContextID" + CMN_TAIL; + String strValReq = CMN_HDR + "validateRequest" + CMN_TAIL; + + // See if we can successfully call into the servlet! + invokeServlet(servletPath, "POST", "CheckMsgInfoKey"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strAuthCtxt, strValReq)); + + boolean bIs115Compatible = true; + + // ONLY if we are Jakarta Authorization compatible, we want to make an additional key test + if (bIs115Compatible) { + String HDR = "layer=HttpServlet appContext=" + appContext; + String str1 = HDR + " Key=jakarta.security.jacc.PolicyContext does exist thus 115 compatible"; + + invokeServlet(servletPath, "POST", "CheckMsgInfoKey"); + + // verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContains(str1)); + + } else { + // Not Jakarta Authorization compatible so the identity must include the caller Principal + // (established during the validateRequest processing using the + // corresponding CallerPrincipalCallback) + // + // Note: by testing the subject=non-null, we are actually verifying that + // assertion JASPIC:SPEC:103 is met (e.g. non-null clientSubject used in + // CallerPrincipalCallback) + String strMsg1 = "In HttpServlet : ServerRuntime CallerPrincipalCallback"; + strMsg1 += " called for profile=HttpServlet for servletPath=" + servletPath; + strMsg1 += " subject=non-null principal set = j2ee"; + + assertTrue(logProcessor.verifyLogContains(strMsg1)); + } + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckServletAppContext + * + * @assertion_ids: JASPIC:SPEC:67; JASPIC:SPEC:68; JASPIC:JAVADOC:84; JASPIC:JAVADOC:85; + * + * @test_Strategy: 1. Verify TSAuthConfigProviderServlet was registered with layer="HttpServlet" and that + * TSServerAuthConfig was invoked with this layer value also to satisfy assertion 67. + * + * 2. Our ProviderConfiguration should have registered a provider for us AND doing that should have caused the MPR to + * use the same appContextId for the ACP and ServerAuthConfig. We will check that both were invoked with the + * same/expected appContextId to satisfy assertion 68. + * + * Description: This test will verify that the runtime uses the same appContextId for both the AuthConfigProvider and + * the ServerAuthConfig. + * + */ + @Test + public void CheckServletAppContext() { + String acfMsg = "getConfigProvider called for Layer : " + "HttpServlet" + " and AppContext :" + appContext; + String sacMsg = "TSServerAuthConfig called for layer=" + "HttpServlet" + " : appContext=" + appContext; + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify TSAuthConfigProviderServlet was registered properly + assertTrue(logProcessor.verifyLogContains(acfMsg)); + + // Verify runtime used same appContextId for the AuthConfigProvider and ServerAuthConfig + assertTrue(logProcessor.verifyLogContains(sacMsg)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckACPConfigObjAppContext + * + * @assertion_ids: JASPIC:SPEC:74; JASPIC:SPEC:75; JASPIC:SPEC:11; JASPIC:SPEC:13; JASPIC:JAVADOC:94 + * + * @test_Strategy: 1. Verify that a non-null provider was returned during our registration of + * TSAuthConfigProviderServlet and that by fact that non-null provider was returned, we need to verify that the MPR + * called getServerAuthConfig for that provider. All these verifications need to be done for the corresponding layer and + * appContextId values. (this satisfys assertion 75) + * + * 2. Verify the MPR called getConfigProvider for the layer and appContextId in order to satisfy assertion 74) + * + * Description: This test will verify that the ACP was properly registered and that the MPR was able to successfully use + * getConfigPRovider. + * + */ + @Test + public void CheckACPConfigObjAppContext() { + String sacMsg = "TSAuthConfigProviderServlet.getServerAuthConfig" + " : layer=" + "HttpServlet" + " : appContext=" + appContext; + + // Make sure everything we need is properly setup + invokeServlet(servletPath, "POST", "CheckACPConfigObjAppContext"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify non-null provider returned during registration + assertTrue(logProcessor.verifyLogContains(ACF_MSG_1)); + + // verify MPR called getConfigProvider for the layer and appContextId + assertTrue(logProcessor.verifyLogContains(sacMsg)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckACPContextObjAppContext + * + * @assertion_ids: JASPIC:SPEC:74; JASPIC:SPEC:75; JASPIC:SPEC:77 + * + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Read the server side log to verify the following: - verify non-null ACP is returned by ACF + * - verify same info used to initialize ACP was also used to initialize the ServerAuthConfig (SAC) for this provider. - + * verify same info used in ACP and SAC was also used to init the auth context. + * + * Description: This test verifies that AuthConfigFactory returns a non-null ACP and that the same info is used to init + * the ACP, SAC, and the ServerAuthContext. + * + */ + @Test + public void CheckACPContextObjAppContext() { + String sacMsg = "TSServerAuthConfig called for layer=" + "HttpServlet" + " : appContext=" + appContext; + String saConfigMsg = "TSServerAuthContext called for messageLayer=" + "HttpServlet" + " : appContext=" + appContext; + + // Make sure everything we need is properly setup + invokeServlet(servletPath, "POST", "CheckACPContextObjAppContext"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // verify non-null ACP is returned by ACF + assertTrue(logProcessor.verifyLogContains(ACF_MSG_1)); + + // verify same info used to initialize ACP was also used to init SAC + assertTrue(logProcessor.verifyLogContains(sacMsg)); + + // verify same info used in ACP and SAC was also used to init auth context + assertTrue(logProcessor.verifyLogContains(saConfigMsg)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckMPRCallsGetAuthContext + * + * @assertion_ids: JASPIC:SPEC:74; JASPIC:SPEC:78; JASPIC:JAVADOC:100 + * + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify the following: - verify non-null ACP is returned by ACF + * - verify ServerAuthConfig.getAuthContext() is called by MPR + * + * Description: This test is used to test that the MPR properly called ServerAuthConfig.getAuthContext(). This will also + * perform an additional test of verifying a non-null ACP is returned by ACF. + * + */ + @Test + public void CheckMPRCallsGetAuthContext() { + String layer = "HttpServlet"; + String saConfigMsg = "TSServerAuthConfig.getAuthContext: layer=" + layer + " : appContext=" + appContext; + + // Make sure everything we need is properly setup + invokeServlet(servletPath, "POST", "CheckMPRCallsGetAuthContext"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Verify non-null ACP is returned by ACF + assertTrue(logProcessor.verifyLogContains(ACF_MSG_1)); + + // Verify TSServerAuthConfig.getAuthContext() is called by the runtime + assertTrue(logProcessor.verifyLogContains(saConfigMsg)); + } + + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckforNonNullAuthContext + * + * @assertion_ids: JASPIC:SPEC:79; JASPIC:JAVADOC:100 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify the following: - verify there were no null values + * returned for all calls to getAuthContext() regardless of what operationId is. (this satisfies assertion 79) - verify + * we ARE seeing at least one successful value returned from our call to getAuthContext() to satisfy oursleves that the + * test really is working correctly as expected. + * + * Description: This test verifies that there were no null instances of ServerAuthContext objects AND we are also + * verifying that we' are getting at least one non-null ServerAuthContext instanace. + * + */ + @Test + public void CheckforNonNullAuthContext() { + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // This string should NOT/NEVER appear in the log file as + // the spec states: + // "For all values of the operation argument, the call to + // getAuthContext must return a non-null authentication context" + assertFalse(logProcessor.verifyLogContains("TSServerAuthConfig.getAuthContext: returned null ServerAuthContext")); + + // We know we should have gotten at least one successful (non-null) + // value from our call go getAuthContext so lets check to make sure. + assertTrue(logProcessor.verifyLogContains("TSServerAuthConfig.getAuthContext: returned non-null ServerAuthContext")); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: CheckforNonNullCallback + * + * @assertion_ids: JASPIC:SPEC:71 + * + * @test_Strategy: + * 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Read the server side log to verify the following: - verify there was a non-null cbh passed + * into the TSAuthConfigProviderServlet.getServerAuthConfig() method. (spec section 3.5 says the cbh must not be null) + * + * Description: This test verifies that a non-null CakkbackHandler instance passed into the + * AuthConfigProvider.getServerAuthConfig(). + * + */ + @Test + public void CheckforNonNullCallback() { + String strMsg1 = "layer=HttpServlet appContext=" + appContext; + strMsg1 += " getServerAuthConfig() received CallbackHandler=non-null"; + + // make sure everything we need is properly setup + invokeServlet(servletPath, "POST", "CheckforNonNullCallback"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + assertTrue(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: VerifyNoInvalidEntries + * + * @assertion_ids: JASPIC:SPEC:52; JASPIC:SPEC:96; JASPIC:SPEC:53; JASPIC:SPEC:87; JASPIC:SPEC:313; JASPIC:SPEC:60; + * JASPIC:JAVADOC:106; JASPIC:SPEC:322; JASPIC:SPEC:323; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Invoke a series of web pages with varying servlet perms and access/connection scenarios. + * + * 3. Use FetchLog servlet to read the server side log to verify no invalid scenarios were encountered. + * + * + * Description This is a negative test case that is used to assist in verifying several different assertions. The + * intention of this test is to make sure accessing web resources under different circumstances should NOT cause any + * invalid issues to be encountered. + * + */ + @Test + public void VerifyNoInvalidEntries() { + // Try to invoke different servlet pages to see if this + // causes an invalid condition to occur + invokeServlet(staticPagePath, "POST", "VerifyNoInvalidEntries"); + invokeServlet(noConstraintPath, "POST", "VerifyNoInvalidEntries"); + invokeServlet(servletPath, "POST", "VerifyNoInvalidEntries"); + + LogFileProcessor logProcessor = new LogFileProcessor(); + logProcessor.fetchLogs(); + + // Now check log file looking for invalid entries + doCommonVerificationChecks(logProcessor); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: ACFSwitchFactorys + * + * @assertion_ids: + * + * + * @test_Strategy: This test does the following: - gets current (CTS) factory - sets the vendors ACF thus replacing the + * CTS ACF - verify ACF's were correctly set - reset factory back to the original CTS factory + * + * 1. Use the static setFactory method to set an ACF and this should always work. and use the getFactory to verify it + * worked. + * + * Description + * + */ + @Test + public void ACFSwitchFactorys() { + String strMsg1 = "ACFTestServlet->ACFSwitchFactorys() passed"; + + String str = invokeServletAndGetResponse(acfServletPath, "POST", "ACFSwitchFactorys"); + + assertTrue(str.contains(strMsg1)); + } + + /** + * + * @keywords: jaspic_servlet + * + * @testName: ACFRemoveRegistrationWithBadId + * + * @assertion_ids: JASPIC:SPEC:345; + * + * + * @test_Strategy: This test verifies we get a return value of False when invoking ACF.removeRegistration(some_bad_id); + * + * 1. Use the static setFactory method to get an ACF and then attempt to invoke removeRegistration() with an invalid (ie + * non-existant) regId. This should return False (per javadoc). + * + * Description + * + */ + @Test + public void ACFRemoveRegistrationWithBadId() { + String strMsg1 = "ACFTestServlet->ACFRemoveRegistrationWithBadId() passed"; + + String str = invokeServletAndGetResponse(acfServletPath, "POST", "ACFRemoveRegistrationWithBadId"); + + assertTrue(str.contains(strMsg1)); + } + + /** + * This is convenience method that can be called anywhere and is intended to be used to make sure no unwanted log + * messages appear which would indicate a failure. + * + *

+ * To clarify, this means that a particular test case ~may~ cause some + * unexpected error to occur which would result in a log message getting dumped to the log file - even though we may not + * be testing that particular assertion at that given moment. This method can be used as a final pass at the end of each + * test to ensure that no accidental/unexpected errors were encountered. + * + */ + private void doCommonVerificationChecks(LogFileProcessor logProcessor) { + // Checks that should ALWAYS pass + checkForInvalidReqPolicy(logProcessor); + checkForInvalidProtectionPolicyID(logProcessor); + checkForInvalidServiceSubjects(logProcessor); + checkForInvalidClientSubjects(logProcessor); + checkForInvalidMessageInfoObjs(logProcessor); + checkForNullCallback(logProcessor); + checkForNullAuthConfigObj(logProcessor); + checkForAuthenMisMatch(logProcessor); + checkForinvalidMsgInfoMapKey(logProcessor); + } + + /** + * This is used to verify assertion JASPIC:SPEC:52 and JASPIC:SPEC:96 which + * states that A new clientSubject must be instantiated and passed in the call + * to validateRequest. (see spec section 3.8.2) We look for occurances of + * strings that indicate we have a null clientSubject passed into our + * validateRequest and we issue a failure if a match is found. + */ + private void checkForInvalidClientSubjects(LogFileProcessor logProcessor) { + String strMsg2 = "FAILURE detected - ClientSubjects should not be read-only."; + String strMsg3 = "FAILURE detected - ClientSubjects should not be null."; + String strMsg1 = "HttpServlet profile: "; + strMsg1 += "TSServerAuthContext.validateRequest called with null client Subject"; + String tempArgs1[] = { strMsg1, strMsg2, strMsg3 }; + + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs1)); + } + + /** + * This is convenience method that helps verify assertion JASPIC:SPEC:87 which states that for the Servlet profile, + * Calling getTargetPolicies on the request MessagePolicy must return an array containing at least one TargetPolicy. We + * look for a message that will be printed out if any calls to getTargetPolicies return an array with less than one + * TargetPolicy. + * + */ + private void checkForInvalidReqPolicy(LogFileProcessor logProcessor) { + String strMsg1 = "Layer=HttpServlet requestPolicy=invalid"; + strMsg1 += " in TSServerAuthModule.initialize()"; + String tempArgs[] = { strMsg1 }; + + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs)); + } + + /** + * This is convenience method that helps verify assertion JASPIC:SPEC:87 which states that for the Servlet profile, + * Calling the getID() method on the ProtectionPolicy must return a valid ID type (as defined in spec section 3.7.4). We + * look for a message that will be printed out if any calls to getID() returned an invalid ProtectionPolicy ID. + * + */ + private void checkForInvalidProtectionPolicyID(LogFileProcessor logProcessor) { + String strMsg1 = "Layer=HttpServlet Invalid ProtectionPolicy.getID()"; + String tempArgs[] = { strMsg1 }; + + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs)); + } + + /** + * This is convenience method that helps verify assertions related to the validateRequest and secureResponse methods. + * This tests assertion JASPIC:SPEC:53 which states that for the Servlet profile, If a non-null Subject was used to + * acquire the ServerAuthContext, the same Subject must be passed as the serviceSubject in this call. + * + * This also tests assertion JASPIC:SPEC:313 which states that along with the above requirments if a non-null + * serviceSubject is used in this call, it must not be read-only. (see spec section 2.1.5.2). + * + * Also tested are assertions JASPIC:SPEC:61, JASPIC:SPEC:314 and JASPIC:SPEC:313. + * + * We look for occurances of strings that indicate we have a serviceSubject mismatch in our runtime - which we should + * not have. + */ + private void checkForInvalidServiceSubjects(LogFileProcessor logProcessor) { + String strMsg1 = "FAILURE detected - ServiceSubjects should be the same and are not."; + String strMsg5 = "FAILURE detected - SecureResponse ServiceSubjects should be the same and are not."; + String strMsg6 = "FAILURE detected - SecureResponse ServiceSubjects should not be read-only."; + String tempArgs1[] = { strMsg1 }; + String tempArgs5[] = { strMsg5 }; // assertion: JASPIC:SPEC:61 and JASPIC:SPEC:314 + String tempArgs6[] = { strMsg6 }; // assertion: JASPIC:SPEC:313 + + String strMsg2 = "ServiceSubjects correctly matched."; + String strMsg3 = "found a non-null serviceSubject in getAuthContext()"; + String strMsg4 = "FAILURE detected - ServiceSubjects should not be read-only."; + + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs1)); + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs5)); + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs6)); + + if (logProcessor.verifyLogContains(strMsg3)) { + + // We know that there was a non-null subject passed into getAuthContext() + // so we better find strMsg2 in our log file too! + assertTrue(logProcessor.verifyLogContains(strMsg2)); + + // If we made it to here, then we have matching serviceSubjects + // and we should NOT have a read-only service subject + assertFalse(logProcessor.verifyLogContains(strMsg4)); + } + } + + /** + * This is used to verify assertion JASPIC:SPEC:323 which states in spec section 3.8.4 (para 2) + * + */ + private void checkForinvalidMsgInfoMapKey(LogFileProcessor logProcessor) { + String strMsg1 = "ERROR - invalid setting for jakarta.servlet.http.authType = null"; + String strMsg2 = "ERROR - mismatch value set for jakarta.servlet.http.authType and getAuthType()"; + String tempArgs1[] = { strMsg1, strMsg2 }; + + // verify that we had NO mismatches between getAuthType() and + // getRemoteUser() + assertFalse(logProcessor.verifyLogContainsOneOf(tempArgs1)); + } + + /** + * This is used to verify assertion JASPIC:SPEC:322 which states in spec section 3.8.4 (para 1) "Both cases, must also + * ensure that the value returned by calling getAuthType on the HttpServletRequest is consistent in terms of being null + * or non-null with the value returned by getUserPrincipal." + * + */ + private void checkForAuthenMisMatch(LogFileProcessor logProcessor) { + String strMsg1 = "ERROR - HttpServletRequest authentication result mis-match with getAuthType() and getRemoteUser()"; + + // Verify that we had NO mismatches between getAuthType() and + // getRemoteUser() + assertFalse(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * This is used to verify assertion JASPIC:SPEC:16 which states that the AuthConfigProvider must NOT return a null auth + * config object (in the getServerAuthConfig() call) + */ + private void checkForNullAuthConfigObj(LogFileProcessor logProcessor) { + String strMsg1 = "WARNING: getServerAuthConfig() returned null"; + + // verify that we had NO bad callbacks passed in + assertFalse(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * This is used to verify assertion JASPIC:SPEC:71 which states that the cbh passed into + * AuthConfigProvider.getServerAuthConfig() must NOT be null. (see spec section 3.5) + */ + private void checkForNullCallback(LogFileProcessor logProcessor) { + String strMsg1 = "FAILURE: layer=HttpServlet appContext=" + appContext; + strMsg1 += " getServerAuthConfig() received CallbackHandler=null"; + + // verify that we had NO bad callbacks passed in + assertFalse(logProcessor.verifyLogContains(strMsg1)); + } + + /** + * This is used to verify assertion JASPIC:SPEC:60 which states that the runtime must pass the same MessageInfo instance + * (that was passed to validateRequest) into secureRequest. (see spec section 2.1.5.2) + */ + private void checkForInvalidMessageInfoObjs(LogFileProcessor logProcessor) { + String strMsg1 = "FAILURE: MessageInfo object in secureRequest does not"; + strMsg1 += " match the messageInfo object from validateRequest"; + + assertFalse(logProcessor.verifyLogContains(strMsg1)); + + } + + + private void invokeServlet(String path, String method, String test) { + readFromServerWithCredentials(path + "?method.under.test=test", "j2ee", "j2ee"); + } + + private String invokeServletAndGetResponse(String path, String method, String test) { + return readFromServerWithCredentials(path + "?method.under.test=" + test, "j2ee", "j2ee"); + } + + +} \ No newline at end of file diff --git a/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java b/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java new file mode 100644 index 0000000..620f7c7 --- /dev/null +++ b/tck/spi/servlet/src/test/java/ee/jakarta/tck/authentication/test/basic/ServletUnitTest.java @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2022-2022 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authentication.test.basic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactoryForStandalone; +import ee.jakarta.tck.authentication.test.basic.servlet.IdUtil; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import java.util.Collection; +import java.util.logging.Logger; +import org.junit.Test; + +/** + * A small collection of unit tests for testing the AuthConfigFactory. + * + *

+ * This only uses code directly in the test context, and does not start up a server. + * + * @author Arjan Tijms + * + */ +public class ServletUnitTest { + + Logger logger = Logger.getLogger(ServletUnitTest.class.getName()); + + /** + * + * @keywords: jaspic_servlet + * + * @testName: AuthConfigFactorySetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:87; JASPIC:JAVADOC:80 + * + * @test_Strategy: 1. Use the static setFactory method to set an ACF and this should always work. + * + * Description This method uses the getFactory() method to get the current factory, then it uses the setFactory() to + * change the current ACF. This method then verifies that the ACF's were indeed changed. We know that the setFactory() + * works as it is used in the bootstrap process - but this is an additional level of testing that allows us to set the + * factory in a slightly different manner than the bootstrap (eg reading it out of the java.security file. + */ + @Test + public void AuthConfigFactorySetFactory() { + + try { + // Set our AuthConfigFactory to a new/different AuthConfigFactory + TSAuthConfigFactoryForStandalone newAuthConfigFactory = new TSAuthConfigFactoryForStandalone(); + AuthConfigFactory.setFactory(newAuthConfigFactory); + String newAuthConfigFactoryClass = newAuthConfigFactory.getClass().getName(); + + // Verify our calls to getFactory() are getting the newly set factory + // instance and not the original ACF instance + AuthConfigFactory testAuthConfigFactory = AuthConfigFactory.getFactory(); + assertNotNull(testAuthConfigFactory); + + String newlySetACFClass = testAuthConfigFactory.getClass().getName(); + + assertEquals("FAILURE - our current ACF does not match our newly set ACF",newlySetACFClass, newAuthConfigFactoryClass); + + } catch (SecurityException ex) { + ex.printStackTrace(); + } + } + + /** + * @keywords: jaspic_servlet + * + * @testName: getRegistrationContextId + * + * @assertion_ids: JASPIC:JAVADOC:77 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description This will use an appContext value that was used to register a provider, and it will see if it can use the + * AuthConfigFactory.RegistrationContext API to try and access the same appContext value that was used during the + * registration process. + * + */ + @Test + public void getRegistrationContextId() { + String appContext = System.getProperty("logical.hostname.servlet") + " /spitests_servlet_web"; + + // register providers in vendor factory + assertTrue(register( + System.getProperty("log.file.location"), + System.getProperty("provider.configuration.file"), + System.getProperty("vendor.authconfig.factory"))); + + // verify we can access a given provider (any provider) appcontext id + boolean bVerified = false; + + AuthConfigFactory authConfigFactory = null; + + try { + authConfigFactory = AuthConfigFactory.getFactory(); + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + } + + assertNotNull( + "getRegistrationContextId(): Error - call to getFactory() returned null ACF.", + authConfigFactory); + + String[] regIDs = authConfigFactory.getRegistrationIDs(null); + for (int ii = 0; ii < regIDs.length; ii++) { + // loop through the AuthConfigFactory's registration ids + + if (regIDs[ii] != null) { + AuthConfigFactory.RegistrationContext acfReg; + acfReg = authConfigFactory.getRegistrationContext(regIDs[ii]); + if (acfReg != null) { + logger.info("appContext = " + appContext); + logger.info("acfReg.getAppContext() = " + acfReg.getAppContext()); + logger.info("layer = " + acfReg.getMessageLayer()); + String str = acfReg.getAppContext(); + if ((str != null) && (str.equals(appContext))) { + // We found our provider info + logger.info("Found it : RegistrationID for our ACP=" + regIDs[ii]); + bVerified = true; + break; + } + } + } + } + + assertTrue( + "Could not find appContext=" + appContext + " in the ACF's list of registration id info", + bVerified); + } + + /** + * @keywords: jaspic_servlet + * + * @testName: AuthConfigFactoryVerifyPersistence + * + * @assertion_ids: JASPIC:JAVADOC:75 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Load vendor's AuthConfigFactory and make sure the registered providers return properly for the right message layer + * and appContextId + * + * Note: We test the persistance behaviour for vendor's AuthConfigFactory by registering providers from a persisted + * file, then we verify the registrations went correctly. + * + * Description + * + * + */ + @Test + public void AuthConfigFactoryVerifyPersistence() { + boolean verified = false; + + try { + // Register providers in vendor factory + assertTrue(register( + System.getProperty("log.file.location"), + System.getProperty("provider.configuration.file"), + System.getProperty("vendor.authconfig.factory"))); + + // Get system default AuthConfigFactory + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + + if (acf != null) { + logger.info("Default AuthConfigFactory class name = " + acf.getClass().getName()); + + verified = verifyRegistrations(acf); + + } else { + logger.info("Default AuthConfigFactory is null" + " can't verify registrations for TestSuite Providers"); + } + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + e.printStackTrace(); + } + + assertTrue( + "AuthConfigFactoryPersistence failed", + verified); + } + + private boolean register(String logFileLocation, String providerConfigurationFileLocation, String vendorAuthConfigFactoryClass) { + try { + printVerticalIndent(); + + // Get an instance of Vendor's AuthConfigFactory + AuthConfigFactory vendorFactory = getAuthConfigFactory(vendorAuthConfigFactoryClass); + + // Set vendor's AuthConfigFactory + AuthConfigFactory.setFactory(vendorFactory); + + // Get system default AuthConfigFactory (which we just set to vendor) + AuthConfigFactory authConfigFactory = AuthConfigFactory.getFactory(); + + if (authConfigFactory != null) { + logger.info("Default AuthConfigFactory class name = " + authConfigFactory.getClass().getName()); + + } else { + logger.info("Default AuthConfigFactory is null" + " can't register TestSuite Providers with null"); + return false; + } + + /** + * Read the ProviderConfiguration XML file This file contains the list of providers that needs to be loaded by the + * vendor's default AuthConfigFactory + */ + Collection providerConfigurationEntries = + readProviderConfigurationXMLFile(providerConfigurationFileLocation); + + + printVerticalIndent(); + + for (ProviderConfigurationEntry providerConfigurationEntry : providerConfigurationEntries) { + // Obtain each ProviderConfigurationEntry and register it with vendor's default AuthConfigFactory + + if (providerConfigurationEntry != null) { + logger.info("Registering Provider " + providerConfigurationEntry.getProviderClassName() + " ..."); + authConfigFactory.registerConfigProvider( + providerConfigurationEntry.getProviderClassName(), + providerConfigurationEntry.getProperties(), + providerConfigurationEntry.getMessageLayer(), + providerConfigurationEntry.getApplicationContextId(), + providerConfigurationEntry.getRegistrationDescription()); + logger.info("Registration Successful"); + } + } + + printVerticalIndent(); + + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory"; + msg = msg + "or ACF.setFactory(). Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + logger.info("Error Registering TestSuite AuthConfig Providers"); + e.printStackTrace(); + } + + return true; + + } + + /** + * This method instantiates and ruturns a AuthConfigFactory based on the specified className + */ + private AuthConfigFactory getAuthConfigFactory(String className) { + AuthConfigFactory authConfigFactory = null; + + if (className != null) { + try { + authConfigFactory = (AuthConfigFactory) + Class.forName(className, true, Thread.currentThread().getContextClassLoader()) + .getDeclaredConstructor() + .newInstance(); + logger.info("Instantiated AuthConfigFactory for: " + className); + + } catch (Exception e) { + logger.info("Error instantiating vendor's " + "AuthConfigFactory class :" + className); + e.printStackTrace(); + + } + } + + return authConfigFactory; + } + + /* + * Read the provider configuration XML file and registers each provider with Vendor's default AuthConfigFactory + */ + private Collection readProviderConfigurationXMLFile(String providerConfigurationFileLocation) { + Collection providerConfigurationEntriesCollection = null; + + logger.info("Reading TestSuite Providers from :" + providerConfigurationFileLocation); + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + ProviderConfigurationXMLFileProcessor configFileProcessor = + new ProviderConfigurationXMLFileProcessor(providerConfigurationFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + providerConfigurationEntriesCollection = configFileProcessor.getProviderConfigurationEntriesCollection(); + + logger.info("TestSuite Providers read successfully " + "from ProviderConfiguration.xml file"); + + return providerConfigurationEntriesCollection; + + } catch (Exception e) { + logger.info("Error loading Providers"); + e.printStackTrace(); + } + + return null; + } + + private boolean verifyRegistrations(AuthConfigFactory authConfigFactory) { + logger.info("Verifying Provider Registrations ..."); + + String servletAppContext = IdUtil.getAppContextId(JASPICData.LAYER_SERVLET); + + try { + // Get AuthConfigProviderServlet + AuthConfigProvider servletAuthConfigProvider = + authConfigFactory.getConfigProvider(JASPICData.LAYER_SERVLET, servletAppContext, null); + + if (servletAuthConfigProvider != null) { + if (servletAuthConfigProvider.getClass().getName().equals(TSAuthConfigProviderServlet.class.getName())) { + logger.info("TSAuthConfigProviderServlet registered for" + " message layer=HttpServlet" + " and appContextId=" + + servletAppContext); + } else { + logger.info("Wrong provider registerd for " + " message layer=HttpServlet" + " and appContextId=" + servletAppContext); + return false; + } + + } else { + logger.info("Error : No AuthConfigprovider registerd for" + " message layer=HttpServlet" + " and appContextId=" + + servletAppContext); + return false; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + private void printVerticalIndent() { + logger.info("**********************************************************"); + logger.info("\n"); + + } + + +} \ No newline at end of file diff --git a/tck/spi/soap/auth.conf b/tck/spi/soap/auth.conf new file mode 100644 index 0000000..6733343 --- /dev/null +++ b/tck/spi/soap/auth.conf @@ -0,0 +1,34 @@ +reg-entry { + con-entry { + ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigProvider + AuthStatus_SEND_SUCCESS:false + requestPolicy:USER_NAME_PASSWORD + } + reg-ctx { + layer:SOAP + app-ctx:null + description:TestSuite JSR 196 Config Provider + } + reg-ctx { + layer:SOAP + app-ctx:localhost /Hello_web/Hello + description:TestSuite JSR 196 Config Provider + } +} +reg-entry { + con-entry { + ee.jakarta.tck.authentication.test.basic.sam.TSAuthConfigProviderServlet + AuthStatus_SEND_SUCCESS:true + requestPolicy:USER_NAME_PASSWORD + } + reg-ctx { + layer:HttpServlet + app-ctx:server /spitests_servlet_web + description:Registration for TSAuthConfigProviderServlet using spitests_servlet_web + } + reg-ctx { + layer:HttpServlet + app-ctx:server /spitests_servlet_web/WrapperServlet + description:Registration for TSAuthConfigProviderServlet using spitests_servlet_web + } +} diff --git a/tck/spi/soap/pom.xml b/tck/spi/soap/pom.xml new file mode 100644 index 0000000..6e78bb0 --- /dev/null +++ b/tck/spi/soap/pom.xml @@ -0,0 +1,154 @@ + + + + 4.0.0 + + + org.eclipse.ee4j.tck.authentication + jakarta-authentication-tck + 3.1.0-SNAPSHOT + + + spi.soap + war + + Jakarta Authentication TCK - Profile SPI SOAP + + A lot of tests about the nitty gritty regarding the SPI per profile. + + + + + org.jakartaee + jaspic-common + 1.0-SNAPSHOT + + + org.eclipse.ee4j.tck.authentication + spi.common + ${project.version} + + + + jakarta.jws + jakarta.jws-api + 3.0.0 + + + org.glassfish.metro + webservices-rt + 4.0.3 + + + org.glassfish.main.security + webservices.security + 8.0.0-SNAPSHOT + + + jakarta.xml.ws + jakarta.xml.ws-api + 4.0.2 + + + jakarta.xml.soap + jakarta.xml.soap-api + 3.0.2 + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.2 + + + + + spitests_servlet_web + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-dependency-plugin + + + [3.2.0,) + + + unpack + + + + + + + + + + + + + + + + + com.sun.xml.ws + jaxws-maven-plugin + 4.0.2 + + + + generate-sources + + + wsimport + + + ee.jakarta.tck.authentication.test.basic.soap + + + + ${basedir}/src/main/webapp/WEB-INF/HelloService.wsdl + + + http://localhost:8080/spitests_servlet_web/HelloService?wsdl + + true + ${basedir}/src/main/java + 3.0 + + + + + + + diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/AuthFactoryContainerInitializer.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/AuthFactoryContainerInitializer.java new file mode 100644 index 0000000..61f6593 --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/AuthFactoryContainerInitializer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authentication.test.basic.soap; + +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import java.util.Set; + +public class AuthFactoryContainerInitializer implements ServletContainerInitializer { + + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + AuthConfigFactory.setFactory(new TSAuthConfigFactory()); + } + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java new file mode 100644 index 0000000..833e58f --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/Hello.java @@ -0,0 +1,42 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebParam; +import jakarta.jws.WebResult; +import jakarta.jws.WebService; +import jakarta.xml.bind.annotation.XmlSeeAlso; +import jakarta.xml.ws.Action; +import jakarta.xml.ws.RequestWrapper; +import jakarta.xml.ws.ResponseWrapper; + + +/** + * This class was generated by the XML-WS Tools. + * XML-WS Tools 4.0.1 + * Generated source version: 3.0 + * + */ +@WebService(name = "Hello", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/") +@XmlSeeAlso({ + ObjectFactory.class +}) +public interface Hello { + + + /** + * + * @param arg0 + * @return + * returns java.lang.String + */ + @WebMethod + @WebResult(targetNamespace = "") + @RequestWrapper(localName = "sayHelloProtected", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", className = "ee.jakarta.tck.authentication.test.basic.soap.SayHelloProtected") + @ResponseWrapper(localName = "sayHelloProtectedResponse", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", className = "ee.jakarta.tck.authentication.test.basic.soap.SayHelloProtectedResponse") + @Action(input = "http://soap.basic.test.authentication.tck.jakarta.ee/Hello/sayHelloProtectedRequest", output = "http://soap.basic.test.authentication.tck.jakarta.ee/Hello/sayHelloProtectedResponse") + public String sayHelloProtected( + @WebParam(name = "arg0", targetNamespace = "") + String arg0); + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloImpl.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloImpl.java new file mode 100644 index 0000000..cb82bc6 --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloImpl.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +@WebService(name = "Hello", serviceName = "HelloService") +public class HelloImpl { + + @WebMethod + public String sayHelloProtected(String param) { + return "Hello " + param; + } +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java new file mode 100644 index 0000000..311f85a --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/HelloService.java @@ -0,0 +1,94 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import java.net.MalformedURLException; +import java.net.URL; +import javax.xml.namespace.QName; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.WebEndpoint; +import jakarta.xml.ws.WebServiceClient; +import jakarta.xml.ws.WebServiceException; +import jakarta.xml.ws.WebServiceFeature; + + +/** + * This class was generated by the XML-WS Tools. + * XML-WS Tools 4.0.1 + * Generated source version: 3.0 + * + */ +@WebServiceClient(name = "HelloService", targetNamespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", wsdlLocation = "http://localhost:8080/spitests_servlet_web/HelloService?wsdl") +public class HelloService + extends Service +{ + + private static final URL HELLOSERVICE_WSDL_LOCATION; + private static final WebServiceException HELLOSERVICE_EXCEPTION; + private static final QName HELLOSERVICE_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService"); + + static { + URL url = null; + WebServiceException e = null; + try { + url = new URL("http://localhost:8080/spitests_servlet_web/HelloService?wsdl"); + } catch (MalformedURLException ex) { + e = new WebServiceException(ex); + } + HELLOSERVICE_WSDL_LOCATION = url; + HELLOSERVICE_EXCEPTION = e; + } + + public HelloService() { + super(__getWsdlLocation(), HELLOSERVICE_QNAME); + } + + public HelloService(WebServiceFeature... features) { + super(__getWsdlLocation(), HELLOSERVICE_QNAME, features); + } + + public HelloService(URL wsdlLocation) { + super(wsdlLocation, HELLOSERVICE_QNAME); + } + + public HelloService(URL wsdlLocation, WebServiceFeature... features) { + super(wsdlLocation, HELLOSERVICE_QNAME, features); + } + + public HelloService(URL wsdlLocation, QName serviceName) { + super(wsdlLocation, serviceName); + } + + public HelloService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) { + super(wsdlLocation, serviceName, features); + } + + /** + * + * @return + * returns Hello + */ + @WebEndpoint(name = "HelloPort") + public Hello getHelloPort() { + return super.getPort(new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloPort"), Hello.class); + } + + /** + * + * @param features + * A list of {@link jakarta.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the features parameter will have their default values. + * @return + * returns Hello + */ + @WebEndpoint(name = "HelloPort") + public Hello getHelloPort(WebServiceFeature... features) { + return super.getPort(new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloPort"), Hello.class, features); + } + + private static URL __getWsdlLocation() { + if (HELLOSERVICE_EXCEPTION!= null) { + throw HELLOSERVICE_EXCEPTION; + } + return HELLOSERVICE_WSDL_LOCATION; + } + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java new file mode 100644 index 0000000..2c49164 --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/ObjectFactory.java @@ -0,0 +1,83 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import javax.xml.namespace.QName; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.annotation.XmlElementDecl; +import jakarta.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the ee.jakarta.tck.authentication.test.basic.soap package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private static final QName _SayHelloProtected_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "sayHelloProtected"); + private static final QName _SayHelloProtectedResponse_QNAME = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "sayHelloProtectedResponse"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: ee.jakarta.tck.authentication.test.basic.soap + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link SayHelloProtected } + * + * @return + * the new instance of {@link SayHelloProtected } + */ + public SayHelloProtected createSayHelloProtected() { + return new SayHelloProtected(); + } + + /** + * Create an instance of {@link SayHelloProtectedResponse } + * + * @return + * the new instance of {@link SayHelloProtectedResponse } + */ + public SayHelloProtectedResponse createSayHelloProtectedResponse() { + return new SayHelloProtectedResponse(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SayHelloProtected }{@code >} + * + * @param value + * Java instance representing xml element's value. + * @return + * the new instance of {@link JAXBElement }{@code <}{@link SayHelloProtected }{@code >} + */ + @XmlElementDecl(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", name = "sayHelloProtected") + public JAXBElement createSayHelloProtected(SayHelloProtected value) { + return new JAXBElement<>(_SayHelloProtected_QNAME, SayHelloProtected.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link SayHelloProtectedResponse }{@code >} + * + * @param value + * Java instance representing xml element's value. + * @return + * the new instance of {@link JAXBElement }{@code <}{@link SayHelloProtectedResponse }{@code >} + */ + @XmlElementDecl(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/", name = "sayHelloProtectedResponse") + public JAXBElement createSayHelloProtectedResponse(SayHelloProtectedResponse value) { + return new JAXBElement<>(_SayHelloProtectedResponse_QNAME, SayHelloProtectedResponse.class, null, value); + } + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java new file mode 100644 index 0000000..c052d6f --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtected.java @@ -0,0 +1,60 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for sayHelloProtected complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

{@code
+ * 
+ *   
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "sayHelloProtected", propOrder = { + "arg0" +}) +public class SayHelloProtected { + + protected String arg0; + + /** + * Gets the value of the arg0 property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getArg0() { + return arg0; + } + + /** + * Sets the value of the arg0 property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setArg0(String value) { + this.arg0 = value; + } + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java new file mode 100644 index 0000000..47e8d0e --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/SayHelloProtectedResponse.java @@ -0,0 +1,62 @@ + +package ee.jakarta.tck.authentication.test.basic.soap; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for sayHelloProtectedResponse complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

{@code
+ * 
+ *   
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "sayHelloProtectedResponse", propOrder = { + "_return" +}) +public class SayHelloProtectedResponse { + + @XmlElement(name = "return") + protected String _return; + + /** + * Gets the value of the return property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReturn() { + return _return; + } + + /** + * Sets the value of the return property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReturn(String value) { + this._return = value; + } + +} diff --git a/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java new file mode 100644 index 0000000..0cb79e8 --- /dev/null +++ b/tck/spi/soap/src/main/java/ee/jakarta/tck/authentication/test/basic/soap/package-info.java @@ -0,0 +1,2 @@ +@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://soap.basic.test.authentication.tck.jakarta.ee/") +package ee.jakarta.tck.authentication.test.basic.soap; diff --git a/tck/spi/soap/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook b/tck/spi/soap/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook new file mode 100644 index 0000000..1d320b3 --- /dev/null +++ b/tck/spi/soap/src/main/resources/META-INF/services/com.sun.xml.ws.assembler.metro.dev.ClientPipelineHook @@ -0,0 +1 @@ +com.sun.enterprise.security.webservices.client.ClientSecurityPipeCreator \ No newline at end of file diff --git a/tck/spi/soap/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/tck/spi/soap/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 0000000..7a4388c --- /dev/null +++ b/tck/spi/soap/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +ee.jakarta.tck.authentication.test.basic.servlet.AuthFactoryContainerInitializer \ No newline at end of file diff --git a/tck/spi/soap/src/main/webapp/WEB-INF/HelloService.wsdl b/tck/spi/soap/src/main/webapp/WEB-INF/HelloService.wsdl new file mode 100644 index 0000000..7a83418 --- /dev/null +++ b/tck/spi/soap/src/main/webapp/WEB-INF/HelloService.wsdl @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tck/spi/soap/src/main/webapp/WEB-INF/HelloService_schema1.xsd b/tck/spi/soap/src/main/webapp/WEB-INF/HelloService_schema1.xsd new file mode 100644 index 0000000..94b5d77 --- /dev/null +++ b/tck/spi/soap/src/main/webapp/WEB-INF/HelloService_schema1.xsd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tck/spi/soap/src/main/webapp/WEB-INF/soap-web.xml b/tck/spi/soap/src/main/webapp/WEB-INF/soap-web.xml new file mode 100644 index 0000000..c8cbc07 --- /dev/null +++ b/tck/spi/soap/src/main/webapp/WEB-INF/soap-web.xml @@ -0,0 +1,49 @@ + + + + + + soap_app + + + Hello + ee.jakarta.tck.authentication.test.basic.soap.HelloImpl + 0 + + ADM + Administrator + + + + Hello + /Hello + + + + 54 + + + + the administrator role + Administrator + + diff --git a/tck/spi/soap/src/main/webapp/WEB-INF/web.xml b/tck/spi/soap/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..180833e --- /dev/null +++ b/tck/spi/soap/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + spi_servlet + + + BASIC + default + + + diff --git a/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java b/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java new file mode 100644 index 0000000..3ee96ee --- /dev/null +++ b/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapProfileSPITest.java @@ -0,0 +1,878 @@ +package ee.jakarta.tck.authentication.test.basic; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.TSSV_ACF; +import static jakarta.security.auth.message.config.AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY; +import static org.junit.Assert.assertTrue; + +import com.sun.xml.ws.api.client.ClientPipelineHook; +import ee.jakarta.tck.authentication.test.basic.soap.AuthFactoryContainerInitializer; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import ee.jakarta.tck.authentication.test.basic.soap.HelloService; +import ee.jakarta.tck.authentication.test.common.ArquillianBase; +import ee.jakarta.tck.authentication.test.common.logging.client.LogFileProcessor; +import ee.jakarta.tck.authentication.test.common.logging.client.TestUtil; +import jakarta.servlet.ServletContainerInitializer; +import java.io.File; +import java.net.URL; +import java.security.Security; +import java.util.Optional; +import java.util.ServiceLoader; +import javax.xml.namespace.QName; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +/** + * This tests a large number of SPI assertions from the Servlet profile. + * + * @author Arjan Tijms + * + */ +@RunWith(Arquillian.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SoapProfileSPITest extends ArquillianBase { + + private static boolean setUpIsDone = false; + + String logicalHostName = "localhost"; + String expectedAppContextId = "localhost /Hello_web/Hello"; + + // this must be the decoded context path corresponding to the web module + private String contextPath = "/" + JASPICData.SCP_CONTEXT_PATH; + private String openToAllServletPath = "OpenToAllServlet"; + + private String appContext = System.getProperty("logical.hostname.servlet") + " " + contextPath; + + LogFileProcessor logProcessor = new LogFileProcessor(); + LogFileProcessor clientLogProcessor = new LogFileProcessor(true); + + @Deployment(testable = false) + public static Archive createDeployment() { + WebArchive archive = defaultWebArchive("Hello_web"); + archive.addAsServiceProvider(ServletContainerInitializer.class, AuthFactoryContainerInitializer.class); + archive.addAsWebInfResource(resource("soap-web.xml"), "web.xml"); + + File file = new File(System.getProperty("log.file.location"), "authentication-trace-log.xml"); + if (file.exists()) { + System.out.println("deleting " + file.toString()); + file.delete(); + } + + file = new File(System.getProperty("log.file.location"), "authentication-trace-log.xml.lck"); + if (file.exists()) { + file.delete(); + } + + System.out.println(archive.toString(true)); + + return archive; + } + + + @Before + public void runOnce() throws Exception { + if (setUpIsDone) { + return; + } + + Security.setProperty(DEFAULT_FACTORY_SECURITY_PROPERTY, TSSV_ACF); + Optional optionalHook = ServiceLoader.load(ClientPipelineHook.class).findFirst(); + if (optionalHook.isPresent()) { + int a; + a = 4; + } + + HelloService helloService = new HelloService( + new URL(getBase(), "Hello?wsdl"), + new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService")); + + String text = helloService.getHelloPort().sayHelloProtected("Raja"); + TestUtil.logMsg("Got Output : " + text); + + logProcessor.fetchLogs(); + + setUpIsDone = true; + } + + @Before + public void fetchLogs() { + logProcessor.fetchLogs(); + clientLogProcessor.fetchLogs(); + } + + + // ### Client side tests + + /** + * @keywords: jaspic_soap + * + * @testName: ValidateResponse + * + * @assertion_ids: JASPIC:SPEC:42 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether ClientAuthContext.validateResponse() is called + * + * Description The runtime must invoke ClientAuthContext.validateResponse() + * + */ + @Test + public void ValidateResponse() { + // verify whether the log contains required messages. + assertTrue( + "validateResponse failed : " + "ClientAuthContext.validateResponse not called", + clientLogProcessor.verifyLogContains("TSClientAuthContext.validateResponse called")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAuthContext + * + * @assertion_ids: JASPIC:SPEC:34; JASPIC:JAVADOC:72; JASPIC:JAVADOC:97; JASPIC:JAVADOC:98 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether ClientAuthConfig.getAuthContext() is called + * + * Description The runtime must invoke clientAuthConfig().getAuthContext() to obtain the ClientAuthContext. + * + */ + @Test + public void ClientAuthContext() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + "ClientAuthContext failed : " + "clientAuthConfig.getAuthContext not called", + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSClientAuthConfig.getAuthContext: layer=SOAP : appContext=")); + } + + /** + * @testName: NameAndPasswordCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:123; JASPIC:JAVADOC:103 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether CallbackHandler for client runtime supports + * NameCallback and PasswordCallback + * + * Description Unless the client runtime is embedded in a server runtime (e.g.; an invocation of a web service by a + * servlet running in a Servlet container), The CallbackHandler passed to ClientAuthModule.initialize must support the + * following callbacks: NameCallback, PasswordCallback + * + */ + @Test + public void NameAndPasswordCallbackSupport() { + // verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "In SOAP : ClientRuntime CallbackHandler supports NameCallback", + "In SOAP : ClientRuntime CallbackHandler supports PasswordCallback")); + } + + /** + * @testName: ClientRuntimeCommonCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:JAVADOC:35; JASPIC:JAVADOC:36; JASPIC:JAVADOC:49; JASPIC:JAVADOC:51; + * JASPIC:JAVADOC:54; JASPIC:JAVADOC:63; JASPIC:JAVADOC:65; JASPIC:JAVADOC:68; JASPIC:JAVADOC:69; JASPIC:JAVADOC:71; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether CallbackHandler for client runtime supports + * CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * Description + * + * The CallbackHandler passed to the initialize method of an authentication module should support the following + * callbacks, and it must be possible to configure the runtime such that the CallbackHandler passed at module + * initialization module supports the following callbacks (in addition to any others required to be supported by the + * applicable internal profile): CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * + */ + @Test + public void ClientRuntimeCommonCallbackSupport() { + // verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "In SOAP : ClientRuntime CallbackHandler supports CertStoreCallback", + "In SOAP : ClientRuntime CallbackHandler supports PrivateKeyCallback", + "In SOAP : ClientRuntime CallbackHandler supports SecretKeyCallback", + "In SOAP : ClientRuntime CallbackHandler supports TrustStoreCallback")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPClientAuthConfig + * + * @assertion_ids: JASPIC:SPEC:124; JASPIC:JAVADOC:77 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether the arguments(layer and appcontextId) passed to + * obtain AuthConfigProvider is same as the arguments used in calling getClientAuthConfig. + * + * Description + * + * If a non-null AuthConfigProvider is returned (by the call to getConfigProvider), the messaging runtime must call + * getClientAuthConfig on the provider to obtain the authentication context configuration object pertaining to the + * application context at the layer. The layer and appContext arguments of the call to getClientAuthConfig must be the + * same as those used to acquire the provider. + * + * + */ + @Test + public void ACPClientAuthConfig() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :")); + + // ACPClientAuthConfig : Same layer and appContextId used + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: CACRequestResponse + * + * @assertion_ids: JASPIC:SPEC:130; JASPIC:JAVADOC:7; JASPIC:JAVADOC:9; JASPIC:SPEC:19; JASPIC:SPEC:23; JASPIC:SPEC:129; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether for a non-null ClientAuthContext, secureRequest + * and validateResponse are called properly. + * + * Description + * + * If the client runtime obtained a non-null ClientAuthContext by using the operation identifier corresponding to the + * request message, then at point (1) in the message processing model, the runtime must call secureRequest on the + * ClientAuthContext, and at point (4) the runtime must call validateResponse on the ClientAuthContext. + * + * + */ + @Test + public void CACRequestResponse() { + // Verify whether the log contains required messages. + assertTrue(clientLogProcessor.verifyLogContains( + "TSClientAuthConfig.getAuthContext: returned non-null" + " ClientAuthContext for operationId=sayHelloProtected", + "TSClientAuthContext.secureRequest called", + "TSClientAuthContext.validateResponse called")); + } + + + + + /** + * @keywords: jaspic_soap + * + * @testName: ClientRuntimeMessageInfoMap + * + * @assertion_ids: JASPIC:SPEC:133; JASPIC:JAVADOC:7 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether the Map in messageInfo object passed to + * secureRequest and validateResponse contains the right value for key jakarta.xml.ws.wsdl.service + * + * Description This profile requires that the message processing runtime establish the following key-value pairs within + * the Map of the MessageInfo passed in the calls to secureRequest and validateResponse Key=jakarta.xml.ws.wsdl.service + * Value= the value of the qualified service name, represented as a javax.xml.namespace.QName + * + */ + @Test + public void ClientRuntimeMessageInfoMap() { + QName expectedQName = new QName("http://soap.basic.test.authentication.tck.jakarta.ee/", "HelloService"); + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContains( + "TSClientAuthModule.secureRequest messageInfo :" + "jakarta.xml.ws.wsdl.service=" + expectedQName.toString(), + "TSClientAuthModule.validateResponse messageInfo :" + "jakarta.xml.ws.wsdl.service=" + expectedQName.toString())); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAppContextId + * + * @assertion_ids: JASPIC:SPEC:208 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify whether for the client side appilcation context + * Identifier is correctly used by the runtime. + * + * Description A Client application context Identifier must be the String value composed by concatenating the client + * scope identifier, a blank separator character, and the client reference to the service. The clien scope identifier is + * not testable but we can check the client reference to the service. + * + */ + @Test + public void ClientAppContextId() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // Verify whether the log contains required messages. + assertTrue( + clientLogProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getClientAuthConfig called for " + "layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ClientAuthConfig + * + * @assertion_ids: JASPIC:SPEC:11; JASPIC:SPEC:12 ; JASPIC:SPEC:16; + * JASPIC:JAVADOC:92; JASPIC:JAVADOC:93; JASPIC:SPEC:110; + * JASPIC:SPEC:111 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether AuthConfigProvider.ClientAuthConfig is + * called in the server. + * + * Description The runtime must invoke + * AuthConfigProvider.getClientAuthConfig() to obtain the + * AuthConfig. The runtime must also specify + * appropriate(non-null) layer(i.e for this test case "SOAP" + * layer) and application context identifiers in its call to + * getClientAuthConfig. + * + */ + @Test + public void ClientAuthConfig() { + String args[] = { "service/HelloService", getBase() + "Hello" }; + + // verify whether the log contains required messages. + assertTrue( + "ClientAuthConfig failed : " + "AuthConfigProvider.getClientAuthConfig not called", + clientLogProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getClientAuthConfig called for layer=SOAP : appContext=")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SecureRequest + * + * @assertion_ids: JASPIC:SPEC:35; JASPIC:JAVADOC:5; JASPIC:SPEC:36; + * JASPIC:JAVADOC:75 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ClientAuthContext.secureRequest() is called + * + * Description The runtime must invoke + * ClientAuthContext.secureRequest() + * + */ + @Test + public void SecureRequest() { + // Verify whether the log contains required messages. + assertTrue( + "SecureRequest failed : " + "ClientAuthContext.secureRequest not called", + clientLogProcessor.verifyLogContains("TSClientAuthContext.secureRequest called")); + } + + + + // ### Server side ### + + /** + * @keywords: jaspic_soap + * + * @testName: GetConfigProvider + * + * @assertion_ids: JASPIC:SPEC:8; JASPIC:SPEC:14; JASPIC:SPEC:116; JASPIC:SPEC:117; JASPIC:JAVADOC:77; + * JASPIC:JAVADOC:79; JASPIC:JAVADOC:80; JASPIC:JAVADOC:84; JASPIC:JAVADOC:85; JASPIC:JAVADOC:91; JASPIC:SPEC:110; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to verify Whether AuthConfigFactory.getConfigProvider is called + * in the server. + * + * Description The runtime must invoke AuthConfigFactory.getConfigProvider to obtain the AuthConfigProvider. The runtime + * must also specify appropriate(non-null) layer and application context identifiers in its call to getConfigProvider. + * + */ + @Test + public void GetConfigProvider() { + assertTrue( + "GetConfigProvider failed : " + "AuthConfigFactory.getConfigProvider not called", + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider called", + "getConfigProvider called for Layer : SOAP and AppContext :" + expectedAppContextId )); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: GetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:77; + * JASPIC:JAVADOC:80; JASPIC:JAVADOC:84; JASPIC:JAVADOC:79; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether AuthConfigFactory.getConfigProvider is + * called in the server. + * + * Description The runtime must invoke + * AuthConfigFactory.getConfigProvider to obtain the + * AuthConfigProvider. By calling getConfigProvider, we can + * assume getFactory() was called. + * + */ + @Test + public void GetFactory() { + // Verify whether the log contains required messages. + assertTrue( + "GetFactory failed : " + "AuthConfigFactory.getFactory not called", + logProcessor.verifyLogContains("TSAuthConfigFactory.getFactory called Indirectly")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAuthConfig + * + * @assertion_ids: JASPIC:SPEC:11; JASPIC:SPEC:13 ; JASPIC:SPEC:16; + * JASPIC:JAVADOC:95 + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether provider.getServerAuthConfig is called in + * the server. + * + * Description The runtime must invoke + * AuthConfigProvider.getServerAuthConfig() to obtain the + * AuthConfig. The runtime must also specify + * appropriate(non-null) layer(i.e for this test case "SOAP" + * layer) and application context identifiers in its call to + * getServerAuthConfig. + * + */ + @Test + public void ServerAuthConfig() { + // Verify whether the log contains required messages. + assertTrue( + "ServerAuthConfig failed : " + "AuthConfigProvider.getServerAuthConfig not called", + logProcessor.verifyLogContains( + "TSAuthConfigProvider.getServerAuthConfig called for layer=SOAP" + + " : appContext=" + expectedAppContextId)); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ValidateRequest + * + * @assertion_ids: JASPIC:SPEC:50; JASPIC:JAVADOC:16; JASPIC:JAVADOC:17; + * JASPIC:JAVADOC:23; JASPIC:SPEC:23; JASPIC:SPEC:19 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthContext.validateRequest() is + * called + * + * Description The runtime must invoke + * ServerAuthContext.validateRequest() + * + */ + @Test + public void ValidateRequest() { + // Verify whether the log contains required messages. + assertTrue( + "ValidateRequest failed : " + "ServerAuthContext.validateRequest not called", + logProcessor.verifyLogContains("TSServerAuthContext.validateRequest called")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SecureResponse + * + * @assertion_ids: JASPIC:SPEC:59; JASPIC:JAVADOC:16; JASPIC:JAVADOC:17; + * JASPIC:JAVADOC:23; JASPIC:JAVADOC:26 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthContext.secureResponse() is called + * + * Description The runtime must invoke + * ServerAuthContext.secureResponse() + * + */ + @Test + public void SecureResponse() { + // verify whether the log contains required messages. + assertTrue("SecureResponse failed : " + "ServerAuthContext.secureResponse not called", + logProcessor.verifyLogContains("TSServerAuthContext.secureResponse called")); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAuthContext + * + * @assertion_ids: JASPIC:SPEC:34; JASPIC:JAVADOC:100; JASPIC:SPEC:153; + * JASPIC:SPEC:156; JASPIC:JAVADOC:101 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether ServerAuthConfig.getAuthContext() is called + * + * Description The runtime must invoke + * serverAuthConfig().getAuthContext() to obtain the + * ServerAuthContext. + * + */ + @Test + public void ServerAuthContext() { + // verify whether the log contains required messages. + assertTrue( + "TSServerAuthConfig.getAuthContext: layer=SOAP : appContext=" + expectedAppContextId, + logProcessor.verifyLogContains( + "TSServerAuthConfig.getAuthContext: layer=SOAP : appContext=" + expectedAppContextId)); + } + + /** + * @keywords: jaspic_soap + * + * @testName: MessageInfo + * + * @assertion_ids: JASPIC:SPEC:35; JASPIC:SPEC:44; JASPIC:JAVADOC:5; + * JASPIC:SPEC:112; JASPIC:JAVADOC:9; JASPIC:JAVADOC:10; + * JASPIC:JAVADOC:11; JASPIC:JAVADOC:28; JASPIC:SPEC:23; + * JASPIC:SPEC:19; JASPIC:SPEC:36; JASPIC:SPEC:37; + * JASPIC:SPEC:43; JASPIC:SPEC:51; JASPIC:SPEC:113; + * JASPIC:SPEC:131; JASPIC:SPEC:132; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the messageInfo passed to secureRequest() + * validateRequest(), secureResponse() and validateResponse() + * contiains right values for getRequestMessage() and + * getResponseMessage() as per the spec. + * + * 3. clientSubject - a Subject that represents the source of + * the service request, or null. + * + * Description The MessageInfo argument used in any call made + * by the message processing runtime to secureRequest, + * validateResponse, validateRequest, or secureResponse must + * have been initialized such that the non-null objects + * returned by the getRequestMessage and getResponseMessage + * methods of the MessageInfo are an instanceof + * jakarta.xml.soap.SOAPMessage. + * + * + */ + @Test + public void MessageInfo() { + // verify whether the log contains required messages. + assertTrue( + "MessageInfo failed : " + "The request and response messages contains incorrect values", + logProcessor.verifyLogContains( + // "secureRequest : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "validateRequest : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "secureResponse : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + "secureResponse : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage" + + + )); + + // "validateResponse : MessageInfo.getRequestMessage() is of type jakarta.xml.soap.SOAPMessage", + //"validateResponse : MessageInfo.getResponseMessage() is of type jakarta.xml.soap.SOAPMessage" + } + + /** + * @testName: ServerRuntimeCommonCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:JAVADOC:35; JASPIC:JAVADOC:36; + * JASPIC:JAVADOC:49; JASPIC:JAVADOC:51; JASPIC:JAVADOC:54; + * JASPIC:JAVADOC:63; JASPIC:JAVADOC:65; JASPIC:JAVADOC:68; + * JASPIC:JAVADOC:69; JASPIC:JAVADOC:71; JASPIC:JAVADOC:106 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether CallbackHandler for server runtime supports + * CertStoreCallback, PrivateKeyCallback, SecretKeyCallback, + * TrustStoreCallback + * + * Description + * + * The CallbackHandler passed to the initialize method of an + * authentication module should support the following + * callbacks, and it must be possible to configure the runtime + * such that the CallbackHandler passed at module + * initialization module supports the following callbacks (in + * addition to any others required to be supported by the + * applicable internal profile): CertStoreCallback, + * PrivateKeyCallback, SecretKeyCallback, TrustStoreCallback + * + * + */ + @Test + public void ServerRuntimeCommonCallbackSupport() { + // Verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "In SOAP : ServerRuntime CallbackHandler supports CertStoreCallback", + "In SOAP : ServerRuntime CallbackHandler supports PrivateKeyCallback", + "In SOAP : ServerRuntime CallbackHandler supports SecretKeyCallback", + "In SOAP : ServerRuntime CallbackHandler supports TrustStoreCallback")); + } + + /** + * @testName: ServerRuntimeCallbackSupport + * + * @assertion_ids: JASPIC:SPEC:114; JASPIC:SPEC:149; JASPIC:JAVADOC:38; + * JASPIC:JAVADOC:39; JASPIC:JAVADOC:40; JASPIC:JAVADOC:42; + * JASPIC:JAVADOC:43; JASPIC:JAVADOC:44; JASPIC:JAVADOC:46; + * JASPIC:JAVADOC:30; JASPIC:JAVADOC:41; JASPIC:JAVADOC:45 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether CallbackHandler for server runtime supports + * CallerPrincipalCallback, GroupPrincipalCallback and + * PasswordValidationCallback + * + * Description + * + * The CallbackHandler passed to the + * ServerAuthModule.initialize must support the following + * callbacks, + * + * CallerPrincipalCallback, GroupPrincipalCallback, + * PasswordValidationCallback + * + * + */ + @Test + public void ServerRuntimeCallbackSupport() { + // Verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "In SOAP : ServerRuntime CallbackHandler supports CallerPrincipalCallback", + "In SOAP : ServerRuntime CallbackHandler supports GroupPrincipalCallback", + "In SOAP : ServerRuntime CallbackHandler supports PasswordValidationCallback")); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: OperationId + * + * @assertion_ids: JASPIC:SPEC:121; JASPIC:SPEC:125; JASPIC:JAVADOC:73 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the operationId is "sayHelloProtected" + * + * Description + * + * If getOperation returns a non-null operation identifier, + * then the operation identifier must be the String value + * corresponding to the operation name appearing in the + * service description (i.e., WSDL). + * + * When its getOperation method is called, any authentication + * context configuration object obtained for the SOAP layer, + * must attempt to derive the corresponding operation + * identifier value from the request message (within + * messageInfo). + * + * + */ + @Test + public void OperationId() { + assertTrue( + logProcessor.verifyLogContains( + "getAuthContextID() called for layer=SOAP shows AuthContextId=" + + "sayHelloProtected")); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPAuthContext + * + * @assertion_ids: JASPIC:SPEC:125; JASPIC:SPEC:150; JASPIC:JAVADOC:5; + * JASPIC:JAVADOC:28; JASPIC:JAVADOC:73; JASPIC:JAVADOC:79 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the arguments(operation) passed to obtain + * getAuthContext is same as defined in Section 4.7.1 + * + * Description The authentication context identifier used in + * the call to getAuthContext must be equivalent to the value + * that would be acquired by calling getAuthContextID with the + * MessageInfo that will be used in the corresponding call to + * secureRequest (by a client runtime) or validateRequest (by + * a server runtime). + * + */ + @Test + public void ACPAuthContext() { + assertTrue( + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :" + expectedAppContextId, + "TSServerAuthConfig.getAuthContext: layer=SOAP" + " : appContext=" + expectedAppContextId + " operationId=sayHelloProtected")); + + +// String cArgs[] = { "service/HelloService", +// "http://" + hostname + ":" + portnum + "/Hello_web/Hello" }; +// +// // verify whether the log contains required messages. +// logProcessor.verifyLogContainsOneOfSubString(cArgs, +// "TSClientAuthConfig.getAuthContext: layer=SOAP" +// + " : appContext="); + + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACPServerAuthConfig + * + * @assertion_ids: JASPIC:SPEC:150; JASPIC:JAVADOC:79; JASPIC:JAVADOC:94 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify Whether the arguments(layer and appcontextId) passed + * to obtain AuthConfigProvider is same as the arguments used + * in calling getServerAuthConfig. + * + * Description + * + * If a non-null AuthConfigProvider is returned (by the call + * to getConfigProvider), the messaging runtime must call + * getServerAuthConfig on the provider to obtain the + * authentication context configuration object pertaining to + * the application context at the layer. The layer and + * appContext arguments of the call to getServerAuthConfig + * must be the same as those used to acquire the provider. + * + * + */ + public void ACPServerAuthConfig() { + // verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "TSAuthConfigFactory.getConfigProvider returned non-null provider for" + " Layer : SOAP and AppContext :" + expectedAppContextId, + "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=SOAP : appContext=" + expectedAppContextId )); + } + + /** + * @keywords: jaspic_soap + * + * @testName: SACRequestResponse + * + * @assertion_ids: JASPIC:SPEC:130; JASPIC:JAVADOC:13; JASPIC:JAVADOC:16; + * JASPIC:JAVADOC:17; JASPIC:JAVADOC:23; JASPIC:JAVADOC:26; + * JASPIC:JAVADOC:28; JASPIC:SPEC:155; + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify whether for a non-null ServerAuthContext, + * validateRequest and secureResponse are called properly. + * + * Description + * + * If the server runtime obtained a non-null ServerAuthContext + * by using the operation identifier corresponding to the + * request message, then at point (2) in the message + * processing model, the runtime must call validateRequest on + * the ClientAuthContext, and at point (3) the runtime must + * call secureResponse on the ServerAuthContext. + * + * + */ + public void SACRequestResponse() { + // verify whether the log contains required messages. + assertTrue( + logProcessor.verifyLogContains( + "TSServerAuthConfig.getAuthContext: returned non-null" + " ServerAuthContext for operationId=sayHelloProtected", + "TSServerAuthContext.validateRequest called", + "TSServerAuthContext.secureResponse called" )); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ServerAppContextId + * + * @assertion_ids: JASPIC:SPEC:209 + * + * @test_Strategy: 1. Register TSSV with the AppServer. (See User guide for + * Registering TSSV with your AppServer ). + * + * 2. Use FetchLog servlet to read the server side log to + * verify whether for the server side appilcation context + * Identifier is correctly used by the runtime. + * + * Description A server application context Identifier shall + * be the String value composed by concatenating a logical + * hostname a blank separator character, and the path + * component of the service endpoint URI corresponding to the + * webservice. + */ + @Test + public void ServerAppContextId() { + String args[] = { + logicalHostName + " /Hello_web/Hello" }; + + // verify whether the log contains required messages. + assertTrue(logProcessor.verifyLogContainsOneOfSubString(args, + "TSAuthConfigProvider.getServerAuthConfig called for " + "layer=SOAP : appContext=")); + } + + + +} \ No newline at end of file diff --git a/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java b/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java new file mode 100644 index 0000000..a807597 --- /dev/null +++ b/tck/spi/soap/src/test/java/ee/jakarta/tck/authentication/test/basic/SoapUnitTest.java @@ -0,0 +1,606 @@ +package ee.jakarta.tck.authentication.test.basic; + +import static ee.jakarta.tck.authentication.test.basic.servlet.JASPICData.TSSV_ACF; +import static jakarta.security.auth.message.config.AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationEntry; +import ee.jakarta.tck.authentication.test.basic.sam.ProviderConfigurationXMLFileProcessor; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSAuthConfigFactoryForStandalone; +import ee.jakarta.tck.authentication.test.basic.sam.config.TSRegistrationListener; +import ee.jakarta.tck.authentication.test.basic.servlet.CommonTests; +import ee.jakarta.tck.authentication.test.basic.servlet.JASPICData; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import java.security.Security; +import java.util.Collection; +import java.util.Iterator; +import java.util.logging.Logger; +import org.junit.Before; +import org.junit.Test; + +public class SoapUnitTest { + + Logger logger = Logger.getLogger(SoapUnitTest.class.getName()); + + private String logFileLocation = System.getProperty("log.file.location"); + private String providerConfigFileLocation = System.getProperty("provider.configuration.file"); + private String vendorACFClass = System.getProperty("vendor.authconfig.factory"); + private String soapAppContext = "localhost /Hello_web/Hello"; + + private transient ProviderConfigurationXMLFileProcessor configFileProcessor; + + private Collection providerConfigurationEntriesCollection; + + private CommonTests commonTests = new CommonTests(); + + @Before + public void setup() { + AuthConfigFactory.setFactory(null); + Security.setProperty(DEFAULT_FACTORY_SECURITY_PROPERTY, TSSV_ACF); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFGetFactory + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:335; JASPIC:SPEC:7; + * + * @test_Strategy: This s mainly concerned with testing the runtimes handling of ACF as follows: - get current (CTS) ACF + * - switch to use different (CTS) ACF - verify calls to ACF use the newer/expected ACF - restore original ACF + */ + @Test + public void ACFGetFactory() { + boolean passed = false; + try { + commonTests._ACF_getFactory(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: ACFSwitchFactorys + * + * @assertion_ids: + * + * + * @test_Strategy: This test does the following: - gets current (CTS) factory - sets the vendors ACF thus replacing the + * CTS ACF - verify ACF's were correctly set - reset factory back to the original CTS factory + * + * 1. Use the static setFactory method to set an ACF and this should always work. and use the getFactory to verify it + * worked. + * + * Description + * + */ + @Test + public void ACFSwitchFactorys() { + boolean passed = false; + try { + commonTests._ACFSwitchFactorys(vendorACFClass); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: testACFComesFromSecFile + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:330; + * + * @test_Strategy: This is calling a method on the server(actually servlet) side that will invoke getFactory() to verify + * a non-null facotry instance is returned. It will also verify that the authconfigprovider.factory security property is + * properly set/used. + * + */ + @Test + public void testACFComesFromSecFile() { + boolean passed = false; + try { + commonTests._testACFComesFromSecFile(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFPersistentRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:329; JASPIC:SPEC:331; JASPIC:SPEC:332; JASPIC:SPEC:340; JASPIC:SPEC:341; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register another persistent ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP + * - try to re-register (persistently) standalone provider - verify 2nd attempt at added standalone provider REPLACED + * the first but it should NOT have added another nor failed. - also confirm that regID for standalone ACP stayed the + * same after 1st and 2nd attempt to register standalone ACP - verify that the 2nd re-registering of ACP replaced the + * ACP AND the description. - unregister standalone ACP and setFactory back to CTS default + * + */ + @Test + public void ACFPersistentRegisterOnlyOneACP() { + boolean passed = false; + try { + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, true); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFInMemoryRegisterOnlyOneACP + * + * @assertion_ids: JASPIC:SPEC:334; JASPIC:SPEC:342; JASPIC:SPEC:343; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register (in-memory) ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP - try + * to re-register (in-memory) standalone provider - verify 2nd attempt at added standalone provider REPLACED the first + * but it should NOT have added another nor failed. - also confirm that regID for standalone ACP stayed the same after + * 1st and 2nd attempt to register standalone ACP - verify that the 2nd re-registering of ACP replaced the ACP AND the + * description. - unregister standalone ACP and setFactory back to CTS default + * + */ + @Test + public void ACFInMemoryRegisterOnlyOneACP() { + boolean passed = false; + try { + commonTests._ACFRegisterOnlyOneACP(logFileLocation, providerConfigFileLocation, vendorACFClass, false); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: ACFUnregisterACP + * + * @assertion_ids: JASPIC:SPEC:344; + * + * @test_Strategy: This will make a server (actually servlet) side method call that will do the following: - load + * vendors ACF - (persistent) register of CTS ACP's in the vendors ACF - get list of vendors registered provider ID's - + * register (in-memory) ACP (this is standalone ACP profile) - verify another regId was added for standalone ACP - + * unregister the in-memory ACP we just registered - verify removeRegistration() method call returned proper boolean - + * verify expected # of registry eentries - verify 2nd call to removeRegistration() with regId that was previously + * removed and should expect return val of false + * + */ + @Test + public void ACFUnregisterACP() { + boolean passed = false; + try { + commonTests._ACFUnregisterACP(logFileLocation, providerConfigFileLocation, vendorACFClass); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: ACFRemoveRegistrationWithBadId + * + * @assertion_ids: JASPIC:SPEC:345; + * + * + * @test_Strategy: This test verifies we get a return value of False when invoking ACF.removeRegistration(some_bad_id); + * + * 1. Use the static setFactory method to get an ACF and then attempt to invoke removeRegistration() with an invalid (ie + * non-existant) regId. This should return False (per javadoc). + * + * Description + * + */ + @Test + public void ACFRemoveRegistrationWithBadId() { + boolean passed = false; + try { + commonTests._ACFRemoveRegistrationWithBadId(); + passed = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + assertTrue(passed); + } + + /** + * @keywords: jaspic_soap + * + * @testName: getRegistrationContextId + * + * @assertion_ids: JASPIC:JAVADOC:77 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description This will use an appContext value that was used to register a provider, and it will see if it can use the + * AuthConfigFactory.RegistrationContext API to try and access the same appContext value that was used during the + * registration process. + * + */ + @Test + public void getRegistrationContextId() { + String appContext = "localhost /Hello_web/Hello"; + + // register providers in vendor factory + assertTrue(register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + + // verify we can access a given provider (any provider) appcontext id + boolean bVerified = false; + + AuthConfigFactory acf = null; + + try { + acf = AuthConfigFactory.getFactory(); + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + } + + assertNotNull( + "getRegistrationContextId failed : could not get acf", + acf); + + + String[] regIDs = acf.getRegistrationIDs(null); + for (int ii = 0; ii < regIDs.length; ii++) { + // loop through the ACF's registration ids + + if (regIDs[ii] != null) { + AuthConfigFactory.RegistrationContext acfReg; + acfReg = acf.getRegistrationContext(regIDs[ii]); + if (acfReg != null) { + logger.info("appContext = " + appContext); + logger.info("acfReg.getAppContext() = " + acfReg.getAppContext()); + logger.info("layer = " + acfReg.getMessageLayer()); + String str = acfReg.getAppContext(); + if ((str != null) && (str.equals(appContext))) { + // we found our provider info + logger.info("Found it : RegistrationID for our ACP=" + regIDs[ii]); + bVerified = true; + break; + } + } + } + } + + String msg = "Could not find appContext=" + appContext; + msg += " in the ACF's list of registration id info"; + + assertTrue(msg, bVerified); + + logger.info("TestSuite Providers registration successful"); + } + + /** + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactoryRegistration + * + * @assertion_ids: JASPIC:JAVADOC:80 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Use the system properties to read the TestSuite providers defined in ProviderConfigruation.xml file and register + * them with vendor's authconfig factory. + * + * + * Description + * + * + */ + @Test + public void AuthConfigFactoryRegistration() { + assertTrue( + register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + } + + public boolean register(String logFileLocation, String providerConfigFileLocation, String vendorACFClass) { + try { + printVerticalIndent(); + + // Get an instance of Vendor's AuthConfigFactory + AuthConfigFactory vendorFactory = getVendorAuthConfigFactory(vendorACFClass); + + // Set vendor's AuthConfigFactory + AuthConfigFactory.setFactory(vendorFactory); + + // Get system default AuthConfigFactory + AuthConfigFactory systemAuthConfigFactory = AuthConfigFactory.getFactory(); + + if (systemAuthConfigFactory != null) { + logger.info("Default AuthConfigFactory class name = " + systemAuthConfigFactory.getClass().getName()); + + } else { + logger.severe("Default AuthConfigFactory is null" + " can't register TestSuite Providers with null"); + return false; + } + + /** + * Read the ProviderConfiguration XML file This file contains the list of providers that needs to be loaded by the + * vendor's default AuthConfigFactory + */ + providerConfigurationEntriesCollection = readProviderConfigurationXMLFile(); + + ProviderConfigurationEntry pce = null; + + printVerticalIndent(); + Iterator iterator = providerConfigurationEntriesCollection.iterator(); + while (iterator.hasNext()) { + // obtain each ProviderConfigurationEntry and register it + // with vendor's default AuthConfigFactory + pce = iterator.next(); + + if (pce != null) { + logger.info("Registering Provider " + pce.getProviderClassName() + " ..."); + systemAuthConfigFactory.registerConfigProvider( + pce.getProviderClassName(), + pce.getProperties(), + pce.getMessageLayer(), + pce.getApplicationContextId(), + pce.getRegistrationDescription()); + + logger.info("Registration Successful"); + } + } + + printVerticalIndent(); + + // Check whether the providers are registered for the right message layer + // and appContext + // verifyRegistrations(acf); + + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory"; + msg = msg + "or ACF.setFactory(). Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + logger.info("Error Registering TestSuite AuthConfig Providers"); + e.printStackTrace(); + } + + return true; + + } + + /** + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactoryVerifyPersistence + * + * @assertion_ids: JASPIC:JAVADOC:75 + * + * @test_Strategy: 1. Get System properties log.file.location, provider.configuration.file and vendor.authconfig.factory + * + * 2. Load vendor's AuthConfigFactory and make sure the registered providers return properly for the right message layer + * and appContextId + * + * Note: We test the persistance behaviour for vendor's AuthConfigFactory by registering providers from a persisted + * file, then we verify the registrations went correctly. + * + * Description + * + * + */ + public void AuthConfigFactoryVerifyPersistence() { + try { + + // register providers in vendor factory + assertTrue(register(logFileLocation, providerConfigFileLocation, vendorACFClass)); + + // Get system default AuthConfigFactory + AuthConfigFactory acf = AuthConfigFactory.getFactory(); + + if (acf != null) { + logger.info("Default AuthConfigFactory class name = " + acf.getClass().getName()); + + assertTrue(verifyRegistrations(acf)); + + } else { + logger.severe("Default AuthConfigFactory is null" + " can't verify registrations for TestSuite Providers"); + } + } catch (SecurityException ex) { + // if here we may not have permission to invoke ACF.getFactory... + String msg = "SecurityException: make sure you have permission to call ACF.getFactory."; + msg = msg + " Check your server side security policies."; + logger.info(msg); + ex.printStackTrace(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * + * @keywords: jaspic_soap + * + * @testName: AuthConfigFactorySetFactory + * + * @assertion_ids: JASPIC:SPEC:7; JASPIC:SPEC:10; JASPIC:JAVADOC:87; JASPIC:JAVADOC:80 + * + * @test_Strategy: 1. Use the static setFactory method to set an ACF and this should always work. + * + * Description This method uses the getFactory() method to get the current factory, then it uses the setFactory() to + * change the current ACF. This method then verifies that the ACF's were indeed changed. We know that the setFactory() + * works as it is used in the bootstrap process - but this is an additional level of testing that allows us to set the + * factory in a slightly different manner than the bootstrap (eg reading it out of the java.security file. + */ + @Test + public void AuthConfigFactorySetFactory() { + // Get current AuthConfigFactory + AuthConfigFactory currentAcf = AuthConfigFactory.getFactory(); + + assertNotNull( + "FAILURE - Could not get current AuthConfigFactory.", + currentAcf); + + String currentACFClass = currentAcf.getClass().getName(); + logger.info("currentACFClass = " + currentACFClass); + + // Set our ACF to a new/different AuthConfigFactory + TSAuthConfigFactoryForStandalone newAcf = new TSAuthConfigFactoryForStandalone(); + AuthConfigFactory.setFactory(newAcf); + String newACFClass = newAcf.getClass().getName(); + logger.info("newACFClass = " + newACFClass); + + // Verify our calls to getFactory() are getting the newly set factory + // instance and not the original ACF instance + AuthConfigFactory testAcf = AuthConfigFactory.getFactory(); + + assertNotNull( + "FAILURE - Could not get newly set AuthConfigFactory.", + testAcf); + + String newlySetACFClass = testAcf.getClass().getName(); + logger.info("newlySetACFClass = " + newlySetACFClass); + + assertEquals( + "FAILURE - our current ACF does not match our newly set ACF", + newACFClass, newlySetACFClass); + + logger.info("newlySetACFClass == newACFClass == " + newACFClass); + + // Restore original factory class + AuthConfigFactory.setFactory(currentAcf); + + logger.info("AuthConfigFactorySetFactory : PASSED"); + } + + + + private void printVerticalIndent() { + logger.info("**********************************************************"); + logger.info("\n"); + } + + /** + * This method instantiates and returns a AuthConfigFactory based on the specified className + */ + private AuthConfigFactory getVendorAuthConfigFactory(String className) { + AuthConfigFactory vFactory = null; + + if (className != null) { + try { + vFactory = (AuthConfigFactory) + Class.forName(className, true, Thread.currentThread() + .getContextClassLoader()) + .getDeclaredConstructor() + .newInstance(); + logger.info("Instantiated Vendor's AuthConfigFactory"); + + } catch (Exception e) { + logger.info("Error instantiating vendor's " + "AuthConfigFactory class :" + className); + e.printStackTrace(); + + } + } + + return vFactory; + } + + /* + * Read the provider configuration XML file and registers each provider with Vendor's default AuthConfigFactory + */ + private Collection readProviderConfigurationXMLFile() { + Collection providerConfigurationEntriesCollection = null; + + logger.info("Reading TestSuite Providers from :" + providerConfigFileLocation); + try { + // Given the provider configuration xml file + // This reader parses the xml file and stores the configuration + // entries as a collection. + configFileProcessor = new ProviderConfigurationXMLFileProcessor(providerConfigFileLocation); + + // Retrieve the ProviderConfigurationEntries collection + providerConfigurationEntriesCollection = configFileProcessor.getProviderConfigurationEntriesCollection(); + + logger.info("TestSuite Providers read successfully " + "from ProviderConfiguration.xml file"); + + return providerConfigurationEntriesCollection; + + } catch (Exception e) { + logger.info("Error loading Providers"); + e.printStackTrace(); + } + + return null; + } + + private boolean verifyRegistrations(AuthConfigFactory acf) { + logger.info("Verifying Provider Registrations ..."); + + try { + // Create a Registration listener + RegistrationListener rlis = new TSRegistrationListener(); + + // Get AuthConfigProvider for soap layer + AuthConfigProvider acp = acf.getConfigProvider(JASPICData.LAYER_SOAP, soapAppContext, rlis); + + if (acp != null) { + if (acp.getClass().getName().equals("com.sun.ts.tests.jaspic.tssv.config.TSAuthConfigProvider")) { + logger.info("TSAuthConfigProvider registered for" + " message layer=SOAP" + " and appContextId=" + soapAppContext); + } else { + logger.info("Wrong provider registerd for " + " message layer=SOAP" + " and appContextId=" + soapAppContext); + return false; + + } + + } else { + logger.info( + "Error : No AuthConfigprovider registerd for" + " message layer=SOAP" + " and appContextId=" + soapAppContext); + return false; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + + } +}