/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.server.plugin.controller; import com.google.common.collect.Sets; import com.thoughtworks.go.plugin.access.authentication.AuthenticationExtension; import com.thoughtworks.go.plugin.api.request.DefaultGoPluginApiRequest; import com.thoughtworks.go.plugin.api.response.DefaultGoApiResponse; import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse; import com.thoughtworks.go.plugin.infra.PluginManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static org.apache.commons.lang.StringUtils.isNotBlank; @Controller public class PluginController { public static final String CONTENT_TYPE_HTML = "text/html; charset=UTF-8"; private PluginManager pluginManager; private static final Set<String> BLACK_LISTED_REQUESTS = Sets.newHashSet("go.plugin-settings.get-configuration", "go.plugin-settings.get-view", "go.plugin-settings.validate-configuration", "go.authentication.plugin-configuration", "go.authentication.authenticate-user", "go.authentication.search-user"); @Autowired public PluginController(PluginManager pluginManager) { this.pluginManager = pluginManager; } @RequestMapping(value = "/plugin/interact/{pluginId}/{requestName}", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}) public void handlePluginInteractRequest( @PathVariable String pluginId, @PathVariable String requestName, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!isAuthPlugin(pluginId)) { response.setStatus(SC_FORBIDDEN); response.getWriter().println("Plugin interact endpoint is enabled only for Authentication Plugins"); return; } if (isRestrictedRequestName(requestName)) { response.setStatus(SC_FORBIDDEN); response.getWriter().println(String.format("Plugin interact for '%s' requestName is disallowed.", requestName)); return; } DefaultGoPluginApiRequest apiRequest = new DefaultGoPluginApiRequest(null, null, requestName); apiRequest.setRequestParams(getParameterMap(request)); addRequestHeaders(request, apiRequest); try { GoPluginApiResponse pluginApiResponse = pluginManager.submitTo(pluginId, apiRequest); if (DefaultGoApiResponse.SUCCESS_RESPONSE_CODE == pluginApiResponse.responseCode()) { renderPluginResponse(pluginApiResponse, response); return; } if (DefaultGoApiResponse.REDIRECT_RESPONSE_CODE == pluginApiResponse.responseCode()) { String location = ""; if (hasValueFor(pluginApiResponse, "Location")) { location = pluginApiResponse.responseHeaders().get("Location"); } response.sendRedirect(location); return; } } catch (Exception e) { // handle } throw new RuntimeException("render error page"); } private boolean isRestrictedRequestName(String requestName) { return BLACK_LISTED_REQUESTS.contains(requestName); } private boolean isAuthPlugin(String pluginId) { return pluginManager.isPluginOfType(AuthenticationExtension.EXTENSION_NAME, pluginId); } private Map<String, String> getParameterMap(HttpServletRequest request) { Map<String, String[]> springParameterMap = request.getParameterMap(); Map<String, String> pluginParameterMap = new HashMap<>(); for (String parameterName : springParameterMap.keySet()) { String[] values = springParameterMap.get(parameterName); if (values != null && values.length > 0) { pluginParameterMap.put(parameterName, values[0]); } else { pluginParameterMap.put(parameterName, null); } } return pluginParameterMap; } private void addRequestHeaders(HttpServletRequest request, DefaultGoPluginApiRequest apiRequest) { Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String header = (String) headerNames.nextElement(); String value = request.getHeader(header); apiRequest.addRequestHeader(header, value); } } private void renderPluginResponse(final GoPluginApiResponse response, HttpServletResponse httpServletResponse) throws IOException { String contentType = CONTENT_TYPE_HTML; if (hasValueFor(response, "Content-Type")) { contentType = response.responseHeaders().get("Content-Type"); } httpServletResponse.setHeader("Content-Type", contentType); httpServletResponse.getWriter().write(response.responseBody()); } private boolean hasValueFor(GoPluginApiResponse response, String header) { return response.responseHeaders() != null && isNotBlank(response.responseHeaders().get(header)); } }