James Moger
2013-03-29 ffe9bd0ea959cf768983ff1a3d2de897390016d7
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  */
16 package com.gitblit;
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
JM 28 import javax.servlet.http.HttpServletResponse;
29
30 import com.gitblit.Constants.FederationRequest;
31 import com.gitblit.models.FederationModel;
32 import com.gitblit.models.FederationProposal;
997c16 33 import com.gitblit.models.TeamModel;
831469 34 import com.gitblit.models.UserModel;
4aafd4 35 import com.gitblit.utils.FederationUtils;
df162c 36 import com.gitblit.utils.FileUtils;
831469 37 import com.gitblit.utils.HttpUtils;
JM 38 import com.gitblit.utils.StringUtils;
39 import com.gitblit.utils.TimeUtils;
40
41 /**
42  * Handles federation requests.
43  * 
44  * @author James Moger
45  * 
46  */
93f0b1 47 public class FederationServlet extends JsonServlet {
831469 48
JM 49     private static final long serialVersionUID = 1L;
50
51     public FederationServlet() {
52         super();
53     }
54
55     /**
93f0b1 56      * Processes a federation request.
831469 57      * 
JM 58      * @param request
59      * @param response
60      * @throws javax.servlet.ServletException
61      * @throws java.io.IOException
62      */
93f0b1 63
JM 64     @Override
65     protected void processRequest(javax.servlet.http.HttpServletRequest request,
831469 66             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
JM 67             java.io.IOException {
4aafd4 68         FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
JM 69         logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
70                 request.getRemoteAddr()));
71
72         if (FederationRequest.POKE.equals(reqType)) {
73             // Gitblit always responds to POKE requests to verify a connection
74             logger.info("Received federation POKE from " + request.getRemoteAddr());
75             return;
76         }
77
831469 78         if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
JM 79             logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
80             response.sendError(HttpServletResponse.SC_FORBIDDEN);
81             return;
82         }
83
2c32fd 84         String uuid = GitBlit.getString(Keys.federation.passphrase, "");
831469 85         if (StringUtils.isEmpty(uuid)) {
2c32fd 86             logger.warn(Keys.federation.passphrase
JM 87                     + " is not properly set!  Federation request denied.");
831469 88             response.sendError(HttpServletResponse.SC_FORBIDDEN);
JM 89             return;
90         }
91
92         if (FederationRequest.PROPOSAL.equals(reqType)) {
93             // Receive a gitblit federation proposal
93f0b1 94             FederationProposal proposal = deserialize(request, response, FederationProposal.class);
JM 95             if (proposal == null) {
831469 96                 return;
JM 97             }
997c16 98
dd9ae7 99             // reject proposal, if not receipt prohibited
JM 100             if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) {
101                 logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
102                         proposal.tokenType.name(), proposal.url));
103                 response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
4aafd4 104                 return;
JM 105             }
106
107             // poke the origin Gitblit instance that is proposing federation
108             boolean poked = false;
109             try {
110                 poked = FederationUtils.poke(proposal.url);
111             } catch (Exception e) {
112                 logger.error("Failed to poke origin", e);
113             }
114             if (!poked) {
115                 logger.error(MessageFormat.format("Failed to send federation poke to {0}",
116                         proposal.url));
117                 response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
dd9ae7 118                 return;
JM 119             }
120
2179fb 121             String url = HttpUtils.getGitblitURL(request);
JM 122             GitBlit.self().submitFederationProposal(proposal, url);
831469 123             logger.info(MessageFormat.format(
JM 124                     "Submitted {0} federation proposal to pull {1} repositories from {2}",
dd9ae7 125                     proposal.tokenType.name(), proposal.repositories.size(), proposal.url));
831469 126             response.setStatus(HttpServletResponse.SC_OK);
JM 127             return;
128         }
129
130         if (FederationRequest.STATUS.equals(reqType)) {
131             // Receive a gitblit federation status acknowledgment
132             String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));
133             String identification = MessageFormat.format("{0} ({1})", remoteId,
134                     request.getRemoteAddr());
135
93f0b1 136             // deserialize the status data
JM 137             FederationModel results = deserialize(request, response, FederationModel.class);
138             if (results == null) {
831469 139                 return;
JM 140             }
141
142             // setup the last and netx pull dates
143             results.lastPull = new Date();
144             int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);
145             results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
146
147             // acknowledge the receipt of status
148             GitBlit.self().acknowledgeFederationStatus(identification, results);
149             logger.info(MessageFormat.format(
150                     "Received status of {0} federated repositories from {1}", results
151                             .getStatusList().size(), identification));
152             response.setStatus(HttpServletResponse.SC_OK);
153             return;
154         }
155
156         // Determine the federation tokens for this gitblit instance
4aafd4 157         String token = request.getParameter("token");
831469 158         List<String> tokens = GitBlit.self().getFederationTokens();
JM 159         if (!tokens.contains(token)) {
160             logger.warn(MessageFormat.format(
161                     "Received Federation token ''{0}'' does not match the server tokens", token));
162             response.sendError(HttpServletResponse.SC_FORBIDDEN);
163             return;
164         }
165
166         Object result = null;
167         if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
2179fb 168             String gitblitUrl = HttpUtils.getGitblitURL(request);
dd9ae7 169             result = GitBlit.self().getRepositories(gitblitUrl, token);
831469 170         } else {
JM 171             if (FederationRequest.PULL_SETTINGS.equals(reqType)) {
172                 // pull settings
173                 if (!GitBlit.self().validateFederationRequest(reqType, token)) {
174                     // invalid token to pull users or settings
175                     logger.warn(MessageFormat.format(
176                             "Federation token from {0} not authorized to pull SETTINGS",
177                             request.getRemoteAddr()));
178                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
179                     return;
180                 }
181                 Map<String, String> settings = new HashMap<String, String>();
182                 List<String> keys = GitBlit.getAllKeys(null);
183                 for (String key : keys) {
184                     settings.put(key, GitBlit.getString(key, ""));
185                 }
186                 result = settings;
187             } else if (FederationRequest.PULL_USERS.equals(reqType)) {
188                 // pull users
189                 if (!GitBlit.self().validateFederationRequest(reqType, token)) {
190                     // invalid token to pull users or settings
191                     logger.warn(MessageFormat.format(
192                             "Federation token from {0} not authorized to pull USERS",
193                             request.getRemoteAddr()));
194                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
195                     return;
196                 }
197                 List<String> usernames = GitBlit.self().getAllUsernames();
198                 List<UserModel> users = new ArrayList<UserModel>();
199                 for (String username : usernames) {
200                     UserModel user = GitBlit.self().getUserModel(username);
201                     if (!user.excludeFromFederation) {
202                         users.add(user);
203                     }
204                 }
205                 result = users;
997c16 206             } else if (FederationRequest.PULL_TEAMS.equals(reqType)) {
JM 207                 // pull teams
208                 if (!GitBlit.self().validateFederationRequest(reqType, token)) {
209                     // invalid token to pull teams
210                     logger.warn(MessageFormat.format(
211                             "Federation token from {0} not authorized to pull TEAMS",
212                             request.getRemoteAddr()));
213                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
214                     return;
215                 }
216                 List<String> teamnames = GitBlit.self().getAllTeamnames();
217                 List<TeamModel> teams = new ArrayList<TeamModel>();
218                 for (String teamname : teamnames) {
219                     TeamModel user = GitBlit.self().getTeamModel(teamname);
220                     teams.add(user);
221                 }
222                 result = teams;
df162c 223             } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {
JM 224                 // pull scripts
225                 if (!GitBlit.self().validateFederationRequest(reqType, token)) {
226                     // invalid token to pull script
227                     logger.warn(MessageFormat.format(
228                             "Federation token from {0} not authorized to pull SCRIPTS",
229                             request.getRemoteAddr()));
230                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
231                     return;
232                 }
233                 Map<String, String> scripts = new HashMap<String, String>();
234                 
235                 Set<String> names = new HashSet<String>();
236                 names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts));
237                 names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts));
238                 for (TeamModel team :  GitBlit.self().getAllTeams()) {
239                     names.addAll(team.preReceiveScripts);
240                     names.addAll(team.postReceiveScripts);
241                 }
242                 File scriptsFolder = GitBlit.getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");
243                 for (String name : names) {
244                     File file = new File(scriptsFolder, name);
245                     if (!file.exists() && !file.getName().endsWith(".groovy")) {
246                         file = new File(scriptsFolder, name + ".groovy");
247                     }
248                     if (file.exists()) {
249                         // read the script
250                         String content = FileUtils.readContent(file, "\n");
251                         scripts.put(name, content);
252                     } else {
253                         // missing script?!
254                         logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));
255                     }
256                 }
257                 result = scripts;
831469 258             }
JM 259         }
260
93f0b1 261         // send the result of the request
JM 262         serialize(response, result);
831469 263     }
JM 264 }