James Moger
2013-11-21 7bf6e183ff8abd0c35eeb29f399da12389562ecb
commit | author | age
831469 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
7bf6e1 16 package com.gitblit.servlet;
831469 17
df162c 18 import java.io.File;
831469 19 import java.text.MessageFormat;
JM 20 import java.util.ArrayList;
21 import java.util.Date;
22 import java.util.HashMap;
df162c 23 import java.util.HashSet;
831469 24 import java.util.List;
JM 25 import java.util.Map;
df162c 26 import java.util.Set;
831469 27
cacf8b 28 import javax.inject.Inject;
JM 29 import javax.inject.Singleton;
831469 30 import javax.servlet.http.HttpServletResponse;
JM 31
7bf6e1 32 import com.gitblit.Constants;
JM 33 import com.gitblit.IStoredSettings;
34 import com.gitblit.Keys;
831469 35 import com.gitblit.Constants.FederationRequest;
7bf6e1 36 import com.gitblit.Keys.federation;
JM 37 import com.gitblit.Keys.git;
38 import com.gitblit.Keys.groovy;
db4f6b 39 import com.gitblit.manager.IFederationManager;
JM 40 import com.gitblit.manager.IRepositoryManager;
41 import com.gitblit.manager.IRuntimeManager;
42 import com.gitblit.manager.IUserManager;
831469 43 import com.gitblit.models.FederationModel;
JM 44 import com.gitblit.models.FederationProposal;
997c16 45 import com.gitblit.models.TeamModel;
831469 46 import com.gitblit.models.UserModel;
4aafd4 47 import com.gitblit.utils.FederationUtils;
df162c 48 import com.gitblit.utils.FileUtils;
831469 49 import com.gitblit.utils.HttpUtils;
JM 50 import com.gitblit.utils.StringUtils;
51 import com.gitblit.utils.TimeUtils;
52
53 /**
54  * Handles federation requests.
699e71 55  *
831469 56  * @author James Moger
699e71 57  *
831469 58  */
cacf8b 59 @Singleton
93f0b1 60 public class FederationServlet extends JsonServlet {
831469 61
JM 62     private static final long serialVersionUID = 1L;
63
cacf8b 64     private final IStoredSettings settings;
JM 65
66     private final IUserManager userManager;
67
68     private final IRepositoryManager repositoryManager;
69
70     private final IFederationManager federationManager;
71
72     @Inject
73     public FederationServlet(
74             IRuntimeManager runtimeManager,
75             IUserManager userManager,
76             IRepositoryManager repositoryManager,
77             IFederationManager federationManager) {
78
831469 79         super();
cacf8b 80         this.settings = runtimeManager.getSettings();
JM 81         this.userManager = userManager;
82         this.repositoryManager = repositoryManager;
83         this.federationManager = federationManager;
831469 84     }
JM 85
86     /**
93f0b1 87      * Processes a federation request.
699e71 88      *
831469 89      * @param request
JM 90      * @param response
91      * @throws javax.servlet.ServletException
92      * @throws java.io.IOException
93      */
93f0b1 94
JM 95     @Override
96     protected void processRequest(javax.servlet.http.HttpServletRequest request,
831469 97             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
JM 98             java.io.IOException {
db4f6b 99
4aafd4 100         FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
JM 101         logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
102                 request.getRemoteAddr()));
103
104         if (FederationRequest.POKE.equals(reqType)) {
105             // Gitblit always responds to POKE requests to verify a connection
106             logger.info("Received federation POKE from " + request.getRemoteAddr());
107             return;
108         }
109
db4f6b 110         if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {
831469 111             logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
JM 112             response.sendError(HttpServletResponse.SC_FORBIDDEN);
113             return;
114         }
115
db4f6b 116         String uuid = settings.getString(Keys.federation.passphrase, "");
831469 117         if (StringUtils.isEmpty(uuid)) {
2c32fd 118             logger.warn(Keys.federation.passphrase
JM 119                     + " is not properly set!  Federation request denied.");
831469 120             response.sendError(HttpServletResponse.SC_FORBIDDEN);
JM 121             return;
122         }
123
124         if (FederationRequest.PROPOSAL.equals(reqType)) {
125             // Receive a gitblit federation proposal
93f0b1 126             FederationProposal proposal = deserialize(request, response, FederationProposal.class);
JM 127             if (proposal == null) {
831469 128                 return;
JM 129             }
997c16 130
dd9ae7 131             // reject proposal, if not receipt prohibited
db4f6b 132             if (!settings.getBoolean(Keys.federation.allowProposals, false)) {
dd9ae7 133                 logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
JM 134                         proposal.tokenType.name(), proposal.url));
135                 response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
4aafd4 136                 return;
JM 137             }
138
139             // poke the origin Gitblit instance that is proposing federation
140             boolean poked = false;
141             try {
142                 poked = FederationUtils.poke(proposal.url);
143             } catch (Exception e) {
144                 logger.error("Failed to poke origin", e);
145             }
146             if (!poked) {
147                 logger.error(MessageFormat.format("Failed to send federation poke to {0}",
148                         proposal.url));
149                 response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
dd9ae7 150                 return;
JM 151             }
152
2179fb 153             String url = HttpUtils.getGitblitURL(request);
db4f6b 154             federationManager.submitFederationProposal(proposal, url);
831469 155             logger.info(MessageFormat.format(
JM 156                     "Submitted {0} federation proposal to pull {1} repositories from {2}",
dd9ae7 157                     proposal.tokenType.name(), proposal.repositories.size(), proposal.url));
831469 158             response.setStatus(HttpServletResponse.SC_OK);
JM 159             return;
160         }
161
162         if (FederationRequest.STATUS.equals(reqType)) {
163             // Receive a gitblit federation status acknowledgment
164             String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));
165             String identification = MessageFormat.format("{0} ({1})", remoteId,
166                     request.getRemoteAddr());
167
93f0b1 168             // deserialize the status data
JM 169             FederationModel results = deserialize(request, response, FederationModel.class);
170             if (results == null) {
831469 171                 return;
JM 172             }
173
174             // setup the last and netx pull dates
175             results.lastPull = new Date();
176             int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);
177             results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
178
179             // acknowledge the receipt of status
db4f6b 180             federationManager.acknowledgeFederationStatus(identification, results);
831469 181             logger.info(MessageFormat.format(
JM 182                     "Received status of {0} federated repositories from {1}", results
183                             .getStatusList().size(), identification));
184             response.setStatus(HttpServletResponse.SC_OK);
185             return;
186         }
187
188         // Determine the federation tokens for this gitblit instance
4aafd4 189         String token = request.getParameter("token");
db4f6b 190         List<String> tokens = federationManager.getFederationTokens();
831469 191         if (!tokens.contains(token)) {
JM 192             logger.warn(MessageFormat.format(
193                     "Received Federation token ''{0}'' does not match the server tokens", token));
194             response.sendError(HttpServletResponse.SC_FORBIDDEN);
195             return;
196         }
197
198         Object result = null;
199         if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
2179fb 200             String gitblitUrl = HttpUtils.getGitblitURL(request);
db4f6b 201             result = federationManager.getRepositories(gitblitUrl, token);
831469 202         } else {
JM 203             if (FederationRequest.PULL_SETTINGS.equals(reqType)) {
204                 // pull settings
db4f6b 205                 if (!federationManager.validateFederationRequest(reqType, token)) {
831469 206                     // invalid token to pull users or settings
JM 207                     logger.warn(MessageFormat.format(
208                             "Federation token from {0} not authorized to pull SETTINGS",
209                             request.getRemoteAddr()));
210                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
211                     return;
212                 }
db4f6b 213                 Map<String, String> map = new HashMap<String, String>();
JM 214                 List<String> keys = settings.getAllKeys(null);
831469 215                 for (String key : keys) {
db4f6b 216                     map.put(key, settings.getString(key, ""));
831469 217                 }
db4f6b 218                 result = map;
831469 219             } else if (FederationRequest.PULL_USERS.equals(reqType)) {
JM 220                 // pull users
db4f6b 221                 if (!federationManager.validateFederationRequest(reqType, token)) {
831469 222                     // invalid token to pull users or settings
JM 223                     logger.warn(MessageFormat.format(
224                             "Federation token from {0} not authorized to pull USERS",
225                             request.getRemoteAddr()));
226                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
227                     return;
228                 }
db4f6b 229                 List<String> usernames = userManager.getAllUsernames();
831469 230                 List<UserModel> users = new ArrayList<UserModel>();
JM 231                 for (String username : usernames) {
db4f6b 232                     UserModel user = userManager.getUserModel(username);
831469 233                     if (!user.excludeFromFederation) {
JM 234                         users.add(user);
235                     }
236                 }
237                 result = users;
997c16 238             } else if (FederationRequest.PULL_TEAMS.equals(reqType)) {
JM 239                 // pull teams
db4f6b 240                 if (!federationManager.validateFederationRequest(reqType, token)) {
997c16 241                     // invalid token to pull teams
JM 242                     logger.warn(MessageFormat.format(
243                             "Federation token from {0} not authorized to pull TEAMS",
244                             request.getRemoteAddr()));
245                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
246                     return;
247                 }
db4f6b 248                 List<String> teamnames = userManager.getAllTeamNames();
997c16 249                 List<TeamModel> teams = new ArrayList<TeamModel>();
JM 250                 for (String teamname : teamnames) {
db4f6b 251                     TeamModel user = userManager.getTeamModel(teamname);
997c16 252                     teams.add(user);
JM 253                 }
254                 result = teams;
df162c 255             } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {
JM 256                 // pull scripts
db4f6b 257                 if (!federationManager.validateFederationRequest(reqType, token)) {
df162c 258                     // invalid token to pull script
JM 259                     logger.warn(MessageFormat.format(
260                             "Federation token from {0} not authorized to pull SCRIPTS",
261                             request.getRemoteAddr()));
262                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
263                     return;
264                 }
265                 Map<String, String> scripts = new HashMap<String, String>();
699e71 266
df162c 267                 Set<String> names = new HashSet<String>();
db4f6b 268                 names.addAll(settings.getStrings(Keys.groovy.preReceiveScripts));
JM 269                 names.addAll(settings.getStrings(Keys.groovy.postReceiveScripts));
270                 for (TeamModel team :  userManager.getAllTeams()) {
df162c 271                     names.addAll(team.preReceiveScripts);
JM 272                     names.addAll(team.postReceiveScripts);
273                 }
db4f6b 274                 File scriptsFolder = repositoryManager.getHooksFolder();
df162c 275                 for (String name : names) {
JM 276                     File file = new File(scriptsFolder, name);
277                     if (!file.exists() && !file.getName().endsWith(".groovy")) {
278                         file = new File(scriptsFolder, name + ".groovy");
279                     }
280                     if (file.exists()) {
281                         // read the script
282                         String content = FileUtils.readContent(file, "\n");
283                         scripts.put(name, content);
284                     } else {
285                         // missing script?!
286                         logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));
287                     }
288                 }
289                 result = scripts;
831469 290             }
JM 291         }
292
93f0b1 293         // send the result of the request
JM 294         serialize(response, result);
831469 295     }
JM 296 }