James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
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
cdb2fe 28 import com.google.inject.Inject;
JM 29 import com.google.inject.Singleton;
831469 30 import javax.servlet.http.HttpServletResponse;
JM 31
23e08c 32 import com.gitblit.Constants.FederationRequest;
7bf6e1 33 import com.gitblit.IStoredSettings;
JM 34 import com.gitblit.Keys;
db4f6b 35 import com.gitblit.manager.IFederationManager;
JM 36 import com.gitblit.manager.IRepositoryManager;
37 import com.gitblit.manager.IUserManager;
831469 38 import com.gitblit.models.FederationModel;
JM 39 import com.gitblit.models.FederationProposal;
997c16 40 import com.gitblit.models.TeamModel;
831469 41 import com.gitblit.models.UserModel;
4aafd4 42 import com.gitblit.utils.FederationUtils;
df162c 43 import com.gitblit.utils.FileUtils;
831469 44 import com.gitblit.utils.HttpUtils;
JM 45 import com.gitblit.utils.StringUtils;
46 import com.gitblit.utils.TimeUtils;
47
48 /**
49  * Handles federation requests.
699e71 50  *
831469 51  * @author James Moger
699e71 52  *
831469 53  */
1b34b0 54 @Singleton
93f0b1 55 public class FederationServlet extends JsonServlet {
831469 56
JM 57     private static final long serialVersionUID = 1L;
58
65d5bb 59     private IStoredSettings settings;
cacf8b 60
65d5bb 61     private IUserManager userManager;
cacf8b 62
65d5bb 63     private IRepositoryManager repositoryManager;
cacf8b 64
65d5bb 65     private IFederationManager federationManager;
cacf8b 66
1b34b0 67     @Inject
JM 68     public FederationServlet(
69             IStoredSettings settings,
70             IUserManager userManager,
71             IRepositoryManager repositoryManager,
72             IFederationManager federationManager) {
73
74         this.settings = settings;
75         this.userManager = userManager;
76         this.repositoryManager = repositoryManager;
77         this.federationManager = federationManager;
831469 78     }
JM 79
80     /**
93f0b1 81      * Processes a federation request.
699e71 82      *
831469 83      * @param request
JM 84      * @param response
85      * @throws javax.servlet.ServletException
86      * @throws java.io.IOException
87      */
93f0b1 88
JM 89     @Override
90     protected void processRequest(javax.servlet.http.HttpServletRequest request,
831469 91             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
JM 92             java.io.IOException {
db4f6b 93
4aafd4 94         FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
JM 95         logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
96                 request.getRemoteAddr()));
97
98         if (FederationRequest.POKE.equals(reqType)) {
99             // Gitblit always responds to POKE requests to verify a connection
100             logger.info("Received federation POKE from " + request.getRemoteAddr());
101             return;
102         }
103
db4f6b 104         if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {
831469 105             logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
JM 106             response.sendError(HttpServletResponse.SC_FORBIDDEN);
107             return;
108         }
109
db4f6b 110         String uuid = settings.getString(Keys.federation.passphrase, "");
831469 111         if (StringUtils.isEmpty(uuid)) {
2c32fd 112             logger.warn(Keys.federation.passphrase
JM 113                     + " is not properly set!  Federation request denied.");
831469 114             response.sendError(HttpServletResponse.SC_FORBIDDEN);
JM 115             return;
116         }
117
118         if (FederationRequest.PROPOSAL.equals(reqType)) {
119             // Receive a gitblit federation proposal
93f0b1 120             FederationProposal proposal = deserialize(request, response, FederationProposal.class);
JM 121             if (proposal == null) {
831469 122                 return;
JM 123             }
997c16 124
dd9ae7 125             // reject proposal, if not receipt prohibited
db4f6b 126             if (!settings.getBoolean(Keys.federation.allowProposals, false)) {
dd9ae7 127                 logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
JM 128                         proposal.tokenType.name(), proposal.url));
129                 response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
4aafd4 130                 return;
JM 131             }
132
133             // poke the origin Gitblit instance that is proposing federation
134             boolean poked = false;
135             try {
136                 poked = FederationUtils.poke(proposal.url);
137             } catch (Exception e) {
138                 logger.error("Failed to poke origin", e);
139             }
140             if (!poked) {
141                 logger.error(MessageFormat.format("Failed to send federation poke to {0}",
142                         proposal.url));
143                 response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
dd9ae7 144                 return;
JM 145             }
146
14cbbe 147             String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null);
JM 148             if (StringUtils.isEmpty(gitblitUrl)) {
149                 gitblitUrl = HttpUtils.getGitblitURL(request);
150             }
151             federationManager.submitFederationProposal(proposal, gitblitUrl);
831469 152             logger.info(MessageFormat.format(
JM 153                     "Submitted {0} federation proposal to pull {1} repositories from {2}",
dd9ae7 154                     proposal.tokenType.name(), proposal.repositories.size(), proposal.url));
831469 155             response.setStatus(HttpServletResponse.SC_OK);
JM 156             return;
157         }
158
159         if (FederationRequest.STATUS.equals(reqType)) {
160             // Receive a gitblit federation status acknowledgment
161             String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));
162             String identification = MessageFormat.format("{0} ({1})", remoteId,
163                     request.getRemoteAddr());
164
93f0b1 165             // deserialize the status data
JM 166             FederationModel results = deserialize(request, response, FederationModel.class);
167             if (results == null) {
831469 168                 return;
JM 169             }
170
171             // setup the last and netx pull dates
172             results.lastPull = new Date();
936af6 173             int mins = TimeUtils.convertFrequencyToMinutes(results.frequency, 5);
831469 174             results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
JM 175
176             // acknowledge the receipt of status
db4f6b 177             federationManager.acknowledgeFederationStatus(identification, results);
831469 178             logger.info(MessageFormat.format(
JM 179                     "Received status of {0} federated repositories from {1}", results
180                             .getStatusList().size(), identification));
181             response.setStatus(HttpServletResponse.SC_OK);
182             return;
183         }
184
185         // Determine the federation tokens for this gitblit instance
4aafd4 186         String token = request.getParameter("token");
db4f6b 187         List<String> tokens = federationManager.getFederationTokens();
831469 188         if (!tokens.contains(token)) {
JM 189             logger.warn(MessageFormat.format(
190                     "Received Federation token ''{0}'' does not match the server tokens", token));
191             response.sendError(HttpServletResponse.SC_FORBIDDEN);
192             return;
193         }
194
195         Object result = null;
196         if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
14cbbe 197             String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null);
JM 198             if (StringUtils.isEmpty(gitblitUrl)) {
199                 gitblitUrl = HttpUtils.getGitblitURL(request);
200             }
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 }