| /* | |
| * 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; | |
| 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 javax.servlet.http.HttpServletResponse; | |
| import com.gitblit.Constants.FederationRequest; | |
| 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 | |
| * | |
| */ | |
| public class FederationServlet extends JsonServlet { | |
| private static final long serialVersionUID = 1L; | |
| public FederationServlet() { | |
| super(); | |
| } | |
| /** | |
| * 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 (!GitBlit.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 = GitBlit.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 (!GitBlit.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 url = HttpUtils.getGitblitURL(request); | |
| GitBlit.self().submitFederationProposal(proposal, url); | |
| 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); | |
| results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L)); | |
| // acknowledge the receipt of status | |
| GitBlit.self().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<String> tokens = GitBlit.self().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 = HttpUtils.getGitblitURL(request); | |
| result = GitBlit.self().getRepositories(gitblitUrl, token); | |
| } else { | |
| if (FederationRequest.PULL_SETTINGS.equals(reqType)) { | |
| // pull settings | |
| if (!GitBlit.self().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<String, String> settings = new HashMap<String, String>(); | |
| List<String> keys = GitBlit.getAllKeys(null); | |
| for (String key : keys) { | |
| settings.put(key, GitBlit.getString(key, "")); | |
| } | |
| result = settings; | |
| } else if (FederationRequest.PULL_USERS.equals(reqType)) { | |
| // pull users | |
| if (!GitBlit.self().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<String> usernames = GitBlit.self().getAllUsernames(); | |
| List<UserModel> users = new ArrayList<UserModel>(); | |
| for (String username : usernames) { | |
| UserModel user = GitBlit.self().getUserModel(username); | |
| if (!user.excludeFromFederation) { | |
| users.add(user); | |
| } | |
| } | |
| result = users; | |
| } else if (FederationRequest.PULL_TEAMS.equals(reqType)) { | |
| // pull teams | |
| if (!GitBlit.self().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<String> teamnames = GitBlit.self().getAllTeamnames(); | |
| List<TeamModel> teams = new ArrayList<TeamModel>(); | |
| for (String teamname : teamnames) { | |
| TeamModel user = GitBlit.self().getTeamModel(teamname); | |
| teams.add(user); | |
| } | |
| result = teams; | |
| } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) { | |
| // pull scripts | |
| if (!GitBlit.self().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<String, String> scripts = new HashMap<String, String>(); | |
| Set<String> names = new HashSet<String>(); | |
| names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts)); | |
| names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts)); | |
| for (TeamModel team : GitBlit.self().getAllTeams()) { | |
| names.addAll(team.preReceiveScripts); | |
| names.addAll(team.postReceiveScripts); | |
| } | |
| File scriptsFolder = GitBlit.getFileOrFolder(Keys.groovy.scriptsFolder, "groovy"); | |
| 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); | |
| } | |
| } |