All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cds.feature.ucl.adapter.UclServlet Maven / Gradle / Ivy

The newest version!
package com.sap.cds.feature.ucl.adapter;

import com.sap.cds.impl.parser.JsonParser;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.services.ErrorStatuses;
import com.sap.cds.services.ServiceException;
import com.sap.cds.feature.ucl.services.SpiiContext;
import com.sap.cds.feature.ucl.services.SpiiResult;
import com.sap.cds.feature.ucl.services.UclService;
import com.sap.cds.services.runtime.CdsRuntime;
import com.sap.cds.services.utils.ErrorStatusException;
import com.sap.cds.services.utils.StringUtils;
import com.sap.cds.services.utils.cert.CertValidator;
import com.sap.cds.services.utils.cert.UclAuthUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;

import static com.sap.cds.services.utils.cert.UclAuthUtils.checkAuthorization;

public class UclServlet extends HttpServlet {

	private static final Logger logger = LoggerFactory.getLogger(UclServlet.class);

	private static final String METHOD_PATCH = "PATCH";

	private final UclService uclService;
	private final CdsRuntime runtime;
	private final CertValidator certValidator;

	public UclServlet(CdsRuntime runtime) {
		this.runtime = runtime;
		this.uclService = runtime.getServiceCatalog().getService(UclService.class, UclService.DEFAULT_NAME);
		this.certValidator = UclAuthUtils.createCertValidator(runtime);
	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// by default, HttpServlet does not support PATCH method
		String method = req.getMethod();
		if (method.equals(METHOD_PATCH)) {
			this.doPatch(req, resp);
		} else {
			super.service(req, resp);
		}
	}

	/*
	 * Endpoint for formation notification: PATCH https:////ucl/spii/v1/tenantMappings/{tenantId}.
	 */
	@SuppressWarnings("unchecked")
	protected void doPatch(HttpServletRequest req, HttpServletResponse res) {
		logger.debug("Received call in Spii tenant mapping");

		processRequest(req, res, p -> !p.isEmpty(), () -> {
			String tenantId = getTenantId(req);
			SpiiRequest spiiRequest = SpiiRequest.of((Map)JsonParser.map(new InputStreamReader(req.getInputStream())));
			SpiiContext ctx = spiiRequest.getContext();
			logger.info("Performing operation '{}' for formation '{}' and tenant '{}'", ctx.getOperation(), ctx.getUclFormationName(), tenantId);

			String resultState = "CREATE_READY";
			Map resultConfiguration = new HashMap<>();

			String operation = ctx.getOperation();
			//TODO: Use async SPII API once certificate is provided via OSB
			if("assign".equals(operation)) {
				try {
					SpiiResult result = this.uclService.assign(tenantId, spiiRequest.getContext(), spiiRequest.getReceiverTenant(), spiiRequest.getAssignedTenant());

					if (result.getReady()) {
						resultState = "CREATE_READY";
					} else {
						resultState = "CONFIG_PENDING";
					}
					resultConfiguration = result.getConfiguration();
				} catch (ServiceException e) {
					logger.error("Error while processing operation '{}' for formation '{}' and '{}'", ctx.getOperation(), ctx.getUclFormationName(), tenantId, e);
					resultState = "CREATE_ERROR";
				}
			} else if ("unassign".equals(operation)) {
				try {
					this.uclService.unassign(tenantId, spiiRequest.getContext(), spiiRequest.getReceiverTenant(), spiiRequest.getAssignedTenant());

					resultState = "DELETE_READY";
				} catch(ServiceException e) {
					logger.error("Error while processing operation '{}' for formation '{}' and '{}'", ctx.getOperation(), ctx.getUclFormationName(), tenantId, e);
					resultState = "DELETE_ERROR";
				}
			}

			res.setStatus(HttpServletResponse.SC_OK); // not SC_CREATED!
			setContentType(res, ContentType.APPLICATION_JSON);

			Map response = new HashMap<>();
			response.put("state", resultState);
			response.put("configuration", resultConfiguration);
			Jsonizer.write(res.getWriter(), response);
		});
	}

	private static String getTenantId(HttpServletRequest req) {
		// gets the app_tid from the request path
		String[] segments = req.getPathInfo().split("/");
		if (segments.length < 2 || StringUtils.isEmpty(segments[1])) {
			throw new ErrorStatusException(ErrorStatuses.BAD_REQUEST);
		}
		return segments[1];
	}

	private static void setContentType(HttpServletResponse resp, ContentType contType) {
		resp.setContentType(contType.getMimeType());
		resp.setCharacterEncoding(contType.getCharset().toString());
	}

	@FunctionalInterface
	private interface Processor {
		void process() throws IOException;
	}

	private static void handleException(HttpServletResponse res, Locale locale, ServiceException e) {
		if (e.getErrorStatus().getHttpStatus() >= 500 && e.getErrorStatus().getHttpStatus() < 600) {
			logger.error("Unexpected error", e);
		} else {
			logger.debug("Service exception thrown", e);
		}
		res.setStatus(e.getErrorStatus().getHttpStatus());
		try {
			String message = e.getLocalizedMessage(locale);
			if (message != null) {
				try (PrintWriter writer = res.getWriter()) {
					writer.write(message);
				}
			}
		} catch (IOException e1) {
			logger.error("Failed to write error message to response", e1);
		}
	}

	private void processRequest(HttpServletRequest req, HttpServletResponse res, Predicate pathMatcher, Processor processor) {
		if (pathMatcher.test(req.getPathInfo())) {
			try {
				checkAuthorization(certValidator, runtime);
			} catch (ServiceException e) {
				handleException(res, null, e);
				return;
			} catch (Throwable t) {
				logger.error("Unexpected error", t);
				res.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
				return;
			}


			runtime.requestContext().systemUserProvider()
					.run(requestContext -> {
						try {
							processor.process();
						} catch (ServiceException e) {
							handleException(res, requestContext.getParameterInfo().getLocale(), e);
						} catch (Throwable t) {
							logger.error("Unexpected error", t);
							res.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
						}
					});
		} else {
			res.setStatus(HttpStatus.SC_NOT_FOUND);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy