You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FederationServlet.java 9.4KB

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