package at.gv.egiz.eaaf.modules.auth.sl20.utils; import java.io.IOException; import java.io.StringWriter; import java.net.URISyntaxException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.EaafConstants; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.modules.auth.sl20.Constants; import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; @Slf4j public class SL20ResponseUtils { private static final String PATTERN_PENDING_REQ_ID = "#PENDINGREQID#"; /** * Build a generic SL2.x error-response without redirect to AuthHandler. * * @param response http response object * @param errorCode ErrorCode * @param errorMsg Error message * @throws Exception In case of a message generation error */ public static void buildErrorResponse(final HttpServletResponse response, final String errorCode, final String errorMsg) throws Exception { final ObjectNode error = SL20JsonBuilderUtils.createErrorCommandResult(errorCode, errorMsg); final ObjectNode errorCommand = SL20JsonBuilderUtils .createCommandResponse(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR, error, null); final ObjectNode respContainer = SL20JsonBuilderUtils.createGenericResponse(UUID.randomUUID().toString(), null, null, errorCommand, null); log.trace("SL20 response to VDA: " + respContainer); final StringWriter writer = new StringWriter(); writer.write(respContainer.toString()); final byte[] content = writer.toString().getBytes("UTF-8"); response.setStatus(HttpServletResponse.SC_OK); response.setContentLength(content.length); response.setContentType(ContentType.APPLICATION_JSON.toString()); response.getOutputStream().write(content); } /** * Build a Security-Layer 2.x conform redirect response. * * @param request http request * @param response http response * @param pendingReq Current pending request * @param fullRedirectUrl Endpoint, where the auth. process should be resumed after redirect * @param transactionId SL2.0 transactionId if available * @param authConfig Basic application configuration * @throws IOException In case of a http servlet error * @throws SL20Exception In case of a SL2.0 request generation error * @throws URISyntaxException In case of an invalid Redirect URL */ public static void buildResponse(final HttpServletRequest request, final HttpServletResponse response, IRequest pendingReq, String fullRedirectUrl, String transactionId, IConfiguration authConfig) throws IOException, SL20Exception, URISyntaxException { // create response final Map reqParameters = new HashMap<>(); final URL redirectUrl = new URL(fullRedirectUrl); if (redirectUrl.getQuery() != null) { final String [] elements = redirectUrl.getQuery().split("&"); for (final String element : elements) { final String[] keyValue = element.split("="); if (keyValue.length == 2) { reqParameters.put(keyValue[0], keyValue[1]); } else { log.warn("Ignore parameter with name: {}", keyValue[0]); } } } //reqParameters.put(EaafConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReq.getPendingRequestId()); final ObjectNode callReqParams = SL20JsonBuilderUtils.createCallCommandParameters( fullRedirectUrl.split("\\?")[0], SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_GET, false, reqParameters); final ObjectNode callCommand = SL20JsonBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CALL, callReqParams); // build first redirect command for app final ObjectNode redirectOneParams = SL20JsonBuilderUtils .createRedirectCommandParameters(generateIpcRedirectUrlForDebugging( pendingReq, authConfig.getBasicConfiguration(Constants.CONFIG_PROP_IPC_RETURN_URL)), callCommand, null, true); final ObjectNode redirectOneCommand = SL20JsonBuilderUtils .createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectOneParams); // build second redirect command for IDP final ObjectNode redirectTwoParams = SL20JsonBuilderUtils.createRedirectCommandParameters( fullRedirectUrl, redirectOneCommand, null, false); final ObjectNode redirectTwoCommand = SL20JsonBuilderUtils .createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectTwoParams); // build generic SL2.0 response container final ObjectNode respContainer = SL20JsonBuilderUtils.createGenericRequest(UUID.randomUUID().toString(), transactionId, redirectTwoCommand, null); if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null && request .getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE)) { log.debug("Client request containts 'native client' header ... "); log.trace("SL20 response to VDA: " + respContainer); final StringWriter writer = new StringWriter(); writer.write(respContainer.toString()); final byte[] content = writer.toString().getBytes("UTF-8"); response.setStatus(HttpServletResponse.SC_OK); response.setContentLength(content.length); response.setContentType(ContentType.APPLICATION_JSON.toString()); response.getOutputStream().write(content); } else { log.info("SL2.0 DataURL communication needs http header: '" + SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE + "'"); log.debug("Client request containts is no native client ... "); final URIBuilder clientRedirectUri = new URIBuilder(fullRedirectUrl); response.setStatus(Integer.parseInt(authConfig.getBasicConfiguration(Constants.CONFIG_PROP_HTTP_REDIRECT_CODE, Constants.CONFIG_PROP_HTTP_REDIRECT_CODE_DEFAULT_VALUE))); response.setHeader("Location", clientRedirectUri.build().toString()); } } /** * Generates a IPC redirect URL that is configured on IDP side. * * @return IPC ReturnURL, or null if no URL is configured */ private static String generateIpcRedirectUrlForDebugging(IRequest pendingReq, String ipcRedirectUrlConfig) { if (StringUtils.isNotEmpty(ipcRedirectUrlConfig)) { if (ipcRedirectUrlConfig.contains(PATTERN_PENDING_REQ_ID)) { log.trace("Find 'pendingReqId' pattern in IPC redirect URL. Update url ... "); ipcRedirectUrlConfig = ipcRedirectUrlConfig.replaceAll("#PENDINGREQID#", EaafConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID + "=" + pendingReq.getPendingRequestId()); } return ipcRedirectUrlConfig; } return null; } }