Saml:AuthenticateService
From Adapt
Configure service for SAML Authentication
There are two parts to consuming SAML assertions in a web service. First is checking the low level embedding of the SAML assertion in the soap headers, and second is check any authorization statements in the assertion that you wish to act on.
The previous client call hid a few details of what really happens when you make a saml call. From the introduction, you'll remember that we use holder-of-key to ensure that a client is allowed to use a given assertion. To use holder-of-key, a client must prove that is owns the private key corresponding to the public key in the assertion. According to the OASIS spec,
The attesting entity includes an XML Signature that can be verified with the key information in the <saml:ConfirmationMethod> of the subject statements of the SAML assertion referenced for keyInfo by the Signature
This translates to sign the soap message w/ clients private key and check the signature against the assertion.
Wss4j doesn't know how to natively process SAML assertions, so it breaks it up into two parts, first is the signature verification, and second is the assertion processing. The signature verification checks to make sure the signature on the soap message was signed by a certificate issued by a trusted certificate authority. The trusted authorities are extraced from a supplied keystore (later). The saml assertion processing is limited to packing the assertion into the message context and letting you retrieve it from your call. This means it's up to your call to check that the certificate from the xml signature is the same as the certificate in the assertion.
Create standard webservice and use CachedDoAllReceiver handlers
Create a standard, unauthenticated web service and make sure you can generate stubs and call it from a client. Now, in the deployment descriptor for your web service, you will need to set a requestflow.
<service name="Receiver" provider="java:RPC" attachments="DIME"> <requestFlow> <handler type="java:edu.umiacs.wssec.CachedDoAllReceiver"> <parameter name="action" value="Timestamp Signature SAMLTokenUnsigned"/> </handler> </requestFlow> <parameter name="className" value="edu.umiacs.pawn.receiving.Receiver"/> <parameter name="allowedMethods" value="*"/> <namespace>urn:receiving.pawn.umiacs.edu</namespace> </service>
You can see how on the receiving side, the signature and saml token are seperated.
Prime CachedDoAllReceiver with keystore
Now that you will use cacheddoallreceiver to handle your signature checking, you need to tell it which keystore to use. In your context startup, you should load a keystore (see previous client example) and call
CachedDoAllReceiver.setSignatureKeyStore(ks);
The keystore that you load into the receiver should have all certificate authorities that are used to sign client certificates. Unlike a client, no callback configuration is needed since you're not accessing locked (private key) information.
Initialize SAMLValidator
Now that wss4j is ready to receive signed messages w/ assetions in the headers, the next step is to setup validation of assertions. This is done through the SAMLValidator class in the pawn-ws-sec class. This class will take care of checking that the assertion was issued by a trusted authority and checks that the XML message signature matched the client certificate in the assertion.
public class SampleContext extends edu.umiacs.servlet.StandardContextListener { ... private static SAMLValidator samlValidator; public void onContextStart(ServletContext ctx, Parameters paramInit) throws ServletException, ParameterException { X509Certificate cert = ... ;// some authorities certificate validator = new SAMLValidator(); validator.putCertificate(cert); ... } public static SAMLValidator getSAMLValidator() { return samlValidator; } }
SAMLValidator from webservice
Now, you need to piece it all together in your web service. wss4j will handle the incoming message, and put the cert of the xml message signature as well as the assertion into the ws-security message results. You can pass that entire message context into the saml validator.
/** * web service call */ public void testAuthorization() throws WebServiceFault { checkAuthorization(SAMLRoles.USER, SAMLRoles.SCHEDULER); } /** * authorization check that takes a list of allowed roles */ private void checkAuthorization(String ... roles) throws WebServiceFault { MessageContext msgContext; SAMLAssertion assertion; // get the message context first msgContext = MessageContext.getCurrentContext(); // challenge token if (!SampleContext.getSAMLValidator().validateMessageContext(msgContext) ) { throw new ClientFault(FaultCode.SAML_TOKEN_ERROR,"SAML Token can not be validated"); } // validateMessage loads the assertion it validated into a more convenient place in the message context assertion = (SAMLAssertion) msgContext.getProperty(Wss4jConstants.WSS4J_SAMLTOKEN_PROPERTY); LOG.debug("token passes");
This just certifies that a token has a trusted authority. What you'll probably want to do is to check the authorization information in the token. The SAMLUtilities class has some methods to help you out.
boolean hasRoles(SAMLAssertion assertion, String ... roles);
- assertion - assertion to check roles in.
- roles - list of roles from SAMLRoles that are allowed
extractRoles, extractFilePlans, and extractNameIdentifiers are also available to extract roles, file plan ID's and usernames from saml assertions.
if (SAMLUtilities.hasRoles(assertion, roles)) { return; } for (String role : SAMLUtilities.extractRoles(assertion)) { LOG.debug("Assertion has role: " + role); } // throw fault since we aren't authorized throw new ClientFault(FaultCode.UNPRIVILEDGED_USER,"Client token fails, or not priviledged"); }