/* * Copyright 2011 gitblit.com. * * 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. */ package com.gitblit.servlet; import java.io.File; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.inject.Inject; import com.google.inject.Singleton; import javax.servlet.http.HttpServletResponse; import com.gitblit.Constants.FederationRequest; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IUserManager; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.FileUtils; import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; /** * Handles federation requests. * * @author James Moger * */ @Singleton public class FederationServlet extends JsonServlet { private static final long serialVersionUID = 1L; private IStoredSettings settings; private IUserManager userManager; private IRepositoryManager repositoryManager; private IFederationManager federationManager; @Inject public FederationServlet( IStoredSettings settings, IUserManager userManager, IRepositoryManager repositoryManager, IFederationManager federationManager) { this.settings = settings; this.userManager = userManager; this.repositoryManager = repositoryManager; this.federationManager = federationManager; } /** * Processes a federation request. * * @param request * @param response * @throws javax.servlet.ServletException * @throws java.io.IOException */ @Override protected void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { FederationRequest reqType = FederationRequest.fromName(request.getParameter("req")); logger.info(MessageFormat.format("Federation {0} request from {1}", reqType, request.getRemoteAddr())); if (FederationRequest.POKE.equals(reqType)) { // Gitblit always responds to POKE requests to verify a connection logger.info("Received federation POKE from " + request.getRemoteAddr()); return; } if (!settings.getBoolean(Keys.git.enableGitServlet, true)) { logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests."); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } String uuid = settings.getString(Keys.federation.passphrase, ""); if (StringUtils.isEmpty(uuid)) { logger.warn(Keys.federation.passphrase + " is not properly set! Federation request denied."); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } if (FederationRequest.PROPOSAL.equals(reqType)) { // Receive a gitblit federation proposal FederationProposal proposal = deserialize(request, response, FederationProposal.class); if (proposal == null) { return; } // reject proposal, if not receipt prohibited if (!settings.getBoolean(Keys.federation.allowProposals, false)) { logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}", proposal.tokenType.name(), proposal.url)); response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return; } // poke the origin Gitblit instance that is proposing federation boolean poked = false; try { poked = FederationUtils.poke(proposal.url); } catch (Exception e) { logger.error("Failed to poke origin", e); } if (!poked) { logger.error(MessageFormat.format("Failed to send federation poke to {0}", proposal.url)); response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); return; } String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null); if (StringUtils.isEmpty(gitblitUrl)) { gitblitUrl = HttpUtils.getGitblitURL(request); } federationManager.submitFederationProposal(proposal, gitblitUrl); logger.info(MessageFormat.format( "Submitted {0} federation proposal to pull {1} repositories from {2}", proposal.tokenType.name(), proposal.repositories.size(), proposal.url)); response.setStatus(HttpServletResponse.SC_OK); return; } if (FederationRequest.STATUS.equals(reqType)) { // Receive a gitblit federation status acknowledgment String remoteId = StringUtils.decodeFromHtml(request.getParameter("url")); String identification = MessageFormat.format("{0} ({1})", remoteId, request.getRemoteAddr()); // deserialize the status data FederationModel results = deserialize(request, response, FederationModel.class); if (results == null) { return; } // setup the last and netx pull dates results.lastPull = new Date(); int mins = TimeUtils.convertFrequencyToMinutes(results.frequency, 5); results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L)); // acknowledge the receipt of status federationManager.acknowledgeFederationStatus(identification, results); logger.info(MessageFormat.format( "Received status of {0} federated repositories from {1}", results .getStatusList().size(), identification)); response.setStatus(HttpServletResponse.SC_OK); return; } // Determine the federation tokens for this gitblit instance String token = request.getParameter("token"); List tokens = federationManager.getFederationTokens(); if (!tokens.contains(token)) { logger.warn(MessageFormat.format( "Received Federation token ''{0}'' does not match the server tokens", token)); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } Object result = null; if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) { String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null); if (StringUtils.isEmpty(gitblitUrl)) { gitblitUrl = HttpUtils.getGitblitURL(request); } result = federationManager.getRepositories(gitblitUrl, token); } else { if (FederationRequest.PULL_SETTINGS.equals(reqType)) { // pull settings if (!federationManager.validateFederationRequest(reqType, token)) { // invalid token to pull users or settings logger.warn(MessageFormat.format( "Federation token from {0} not authorized to pull SETTINGS", request.getRemoteAddr())); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } Map map = new HashMap(); List keys = settings.getAllKeys(null); for (String key : keys) { map.put(key, settings.getString(key, "")); } result = map; } else if (FederationRequest.PULL_USERS.equals(reqType)) { // pull users if (!federationManager.validateFederationRequest(reqType, token)) { // invalid token to pull users or settings logger.warn(MessageFormat.format( "Federation token from {0} not authorized to pull USERS", request.getRemoteAddr())); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } List usernames = userManager.getAllUsernames(); List users = new ArrayList(); for (String username : usernames) { UserModel user = userManager.getUserModel(username); if (!user.excludeFromFederation) { users.add(user); } } result = users; } else if (FederationRequest.PULL_TEAMS.equals(reqType)) { // pull teams if (!federationManager.validateFederationRequest(reqType, token)) { // invalid token to pull teams logger.warn(MessageFormat.format( "Federation token from {0} not authorized to pull TEAMS", request.getRemoteAddr())); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } List teamnames = userManager.getAllTeamNames(); List teams = new ArrayList(); for (String teamname : teamnames) { TeamModel user = userManager.getTeamModel(teamname); teams.add(user); } result = teams; } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) { // pull scripts if (!federationManager.validateFederationRequest(reqType, token)) { // invalid token to pull script logger.warn(MessageFormat.format( "Federation token from {0} not authorized to pull SCRIPTS", request.getRemoteAddr())); response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } Map scripts = new HashMap(); Set names = new HashSet(); names.addAll(settings.getStrings(Keys.groovy.preReceiveScripts)); names.addAll(settings.getStrings(Keys.groovy.postReceiveScripts)); for (TeamModel team : userManager.getAllTeams()) { names.addAll(team.preReceiveScripts); names.addAll(team.postReceiveScripts); } File scriptsFolder = repositoryManager.getHooksFolder(); for (String name : names) { File file = new File(scriptsFolder, name); if (!file.exists() && !file.getName().endsWith(".groovy")) { file = new File(scriptsFolder, name + ".groovy"); } if (file.exists()) { // read the script String content = FileUtils.readContent(file, "\n"); scripts.put(name, content); } else { // missing script?! logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name)); } } result = scripts; } } // send the result of the request serialize(response, result); } }