package org.apereo.cas.ws.idp.services; import com.google.common.base.Throwables; import org.apache.commons.lang3.BooleanUtils; import org.apache.cxf.binding.soap.SoapFault; import org.apache.cxf.fediz.core.exception.ProcessingException; import org.apache.cxf.rt.security.SecurityConstants; import org.apache.cxf.staxutils.W3CDOMStreamWriter; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.cxf.ws.security.trust.STSUtils; import org.apereo.cas.CipherExecutor; import org.apereo.cas.authentication.SecurityTokenServiceClient; import org.apereo.cas.authentication.SecurityTokenServiceClientBuilder; import org.apereo.cas.util.CollectionUtils; import org.apereo.cas.ws.idp.WSFederationClaims; import org.apereo.cas.ws.idp.WSFederationConstants; import org.apereo.cas.ws.idp.web.WSFederationRequest; import org.jasig.cas.client.validation.Assertion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import javax.servlet.http.HttpServletRequest; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.StringWriter; import java.util.Collection; /** * This is {@link DefaultRelyingPartyTokenProducer}. * * @author Misagh Moayyed * @since 5.1.0 */ public class DefaultRelyingPartyTokenProducer implements WSFederationRelyingPartyTokenProducer { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRelyingPartyTokenProducer.class); private final SecurityTokenServiceClientBuilder clientBuilder; private final CipherExecutor<String, String> credentialCipherExecutor; public DefaultRelyingPartyTokenProducer(final SecurityTokenServiceClientBuilder securityTokenServiceClientBuilder, final CipherExecutor<String, String> credentialCipherExecutor) { this.clientBuilder = securityTokenServiceClientBuilder; this.credentialCipherExecutor = credentialCipherExecutor; } @Override public String produce(final SecurityToken securityToken, final WSFederationRegisteredService service, final WSFederationRequest fedRequest, final HttpServletRequest request, final Assertion assertion) { final SecurityTokenServiceClient sts = clientBuilder.buildClientForRelyingPartyTokenResponses(securityToken, service); mapAttributesToRequestedClaims(service, sts, assertion); final Element rpToken = requestSecurityTokenResponse(service, sts, assertion); return serializeRelyingPartyToken(rpToken); } private static String serializeRelyingPartyToken(final Element rpToken) { try { final StringWriter sw = new StringWriter(); final Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, BooleanUtils.toStringYesNo(Boolean.TRUE)); t.transform(new DOMSource(rpToken), new StreamResult(sw)); return sw.toString(); } catch (final TransformerException e) { throw Throwables.propagate(e); } } private static void mapAttributesToRequestedClaims(final WSFederationRegisteredService service, final SecurityTokenServiceClient sts, final Assertion assertion) { try { final W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); writer.writeStartElement("wst", "Claims", STSUtils.WST_NS_05_12); writer.writeNamespace("wst", STSUtils.WST_NS_05_12); writer.writeNamespace("ic", WSFederationConstants.HTTP_SCHEMAS_XMLSOAP_ORG_WS_2005_05_IDENTITY); writer.writeAttribute("Dialect", WSFederationConstants.HTTP_SCHEMAS_XMLSOAP_ORG_WS_2005_05_IDENTITY); assertion.getPrincipal().getAttributes().forEach((k, v) -> { try { if (WSFederationClaims.contains(k)) { final String uri = WSFederationClaims.valueOf(k).getUri(); LOGGER.debug("Requesting claim [{}] mapped to [{}]", k, uri); writer.writeStartElement("ic", "ClaimValue", WSFederationConstants.HTTP_SCHEMAS_XMLSOAP_ORG_WS_2005_05_IDENTITY); writer.writeAttribute("Uri", uri); writer.writeAttribute("Optional", Boolean.TRUE.toString()); final Collection vv = CollectionUtils.toCollection(v); for (final Object value : vv) { if (value instanceof String) { writer.writeStartElement("ic", "Value", WSFederationConstants.HTTP_SCHEMAS_XMLSOAP_ORG_WS_2005_05_IDENTITY); writer.writeCharacters((String) value); writer.writeEndElement(); } } writer.writeEndElement(); } } catch (final Exception e) { LOGGER.error(e.getMessage(), e); } }); writer.writeEndElement(); final Element claims = writer.getDocument().getDocumentElement(); sts.setClaims(claims); } catch (final Exception e) { LOGGER.error(e.getMessage(), e); } } private Element requestSecurityTokenResponse(final WSFederationRegisteredService service, final SecurityTokenServiceClient sts, final Assertion assertion) { try { sts.getProperties().put(SecurityConstants.USERNAME, assertion.getPrincipal().getName()); final String uid = credentialCipherExecutor.encode(assertion.getPrincipal().getName()); sts.getProperties().put(SecurityConstants.PASSWORD, uid); return sts.requestSecurityTokenResponse(service.getAppliesTo()); } catch (final SoapFault ex) { if (ex.getFaultCode() != null && "RequestFailed".equals(ex.getFaultCode().getLocalPart())) { throw new RuntimeException(new ProcessingException(ProcessingException.TYPE.BAD_REQUEST)); } throw ex; } catch (final Exception ex) { throw Throwables.propagate(ex); } } }