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.

GitBlit.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*
  2. * Copyright 2013 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.text.MessageFormat;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.Comparator;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Set;
  24. import javax.inject.Singleton;
  25. import javax.servlet.http.HttpServletRequest;
  26. import com.gitblit.Constants.AccessPermission;
  27. import com.gitblit.Constants.Transport;
  28. import com.gitblit.manager.GitblitManager;
  29. import com.gitblit.manager.IAuthenticationManager;
  30. import com.gitblit.manager.IFederationManager;
  31. import com.gitblit.manager.IGitblit;
  32. import com.gitblit.manager.INotificationManager;
  33. import com.gitblit.manager.IPluginManager;
  34. import com.gitblit.manager.IProjectManager;
  35. import com.gitblit.manager.IRepositoryManager;
  36. import com.gitblit.manager.IRuntimeManager;
  37. import com.gitblit.manager.IUserManager;
  38. import com.gitblit.manager.ServicesManager;
  39. import com.gitblit.models.RepositoryModel;
  40. import com.gitblit.models.RepositoryUrl;
  41. import com.gitblit.models.UserModel;
  42. import com.gitblit.tickets.BranchTicketService;
  43. import com.gitblit.tickets.FileTicketService;
  44. import com.gitblit.tickets.ITicketService;
  45. import com.gitblit.tickets.NullTicketService;
  46. import com.gitblit.tickets.RedisTicketService;
  47. import com.gitblit.transport.ssh.IPublicKeyManager;
  48. import com.gitblit.utils.StringUtils;
  49. import dagger.Module;
  50. import dagger.ObjectGraph;
  51. import dagger.Provides;
  52. /**
  53. * GitBlit is the aggregate manager for the Gitblit webapp. It provides all
  54. * management functions and also manages some long-running services.
  55. *
  56. * @author James Moger
  57. *
  58. */
  59. public class GitBlit extends GitblitManager {
  60. private final ObjectGraph injector;
  61. private final ServicesManager servicesManager;
  62. private ITicketService ticketService;
  63. public GitBlit(
  64. IRuntimeManager runtimeManager,
  65. IPluginManager pluginManager,
  66. INotificationManager notificationManager,
  67. IUserManager userManager,
  68. IAuthenticationManager authenticationManager,
  69. IPublicKeyManager publicKeyManager,
  70. IRepositoryManager repositoryManager,
  71. IProjectManager projectManager,
  72. IFederationManager federationManager) {
  73. super(runtimeManager,
  74. pluginManager,
  75. notificationManager,
  76. userManager,
  77. authenticationManager,
  78. publicKeyManager,
  79. repositoryManager,
  80. projectManager,
  81. federationManager);
  82. this.injector = ObjectGraph.create(getModules());
  83. this.servicesManager = new ServicesManager(this);
  84. }
  85. @Override
  86. public GitBlit start() {
  87. super.start();
  88. logger.info("Starting services manager...");
  89. servicesManager.start();
  90. configureTicketService();
  91. return this;
  92. }
  93. @Override
  94. public GitBlit stop() {
  95. super.stop();
  96. servicesManager.stop();
  97. ticketService.stop();
  98. return this;
  99. }
  100. @Override
  101. public boolean isServingRepositories() {
  102. return servicesManager.isServingRepositories();
  103. }
  104. protected Object [] getModules() {
  105. return new Object [] { new GitBlitModule()};
  106. }
  107. protected boolean acceptPush(Transport byTransport) {
  108. if (byTransport == null) {
  109. logger.info("Unknown transport, push rejected!");
  110. return false;
  111. }
  112. Set<Transport> transports = new HashSet<Transport>();
  113. for (String value : getSettings().getStrings(Keys.git.acceptedPushTransports)) {
  114. Transport transport = Transport.fromString(value);
  115. if (transport == null) {
  116. logger.info(String.format("Ignoring unknown registered transport %s", value));
  117. continue;
  118. }
  119. transports.add(transport);
  120. }
  121. if (transports.isEmpty()) {
  122. // no transports are explicitly specified, all are acceptable
  123. return true;
  124. }
  125. // verify that the transport is permitted
  126. return transports.contains(byTransport);
  127. }
  128. /**
  129. * Returns a list of repository URLs and the user access permission.
  130. *
  131. * @param request
  132. * @param user
  133. * @param repository
  134. * @return a list of repository urls
  135. */
  136. @Override
  137. public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) {
  138. if (user == null) {
  139. user = UserModel.ANONYMOUS;
  140. }
  141. String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username);
  142. List<RepositoryUrl> list = new ArrayList<RepositoryUrl>();
  143. // http/https url
  144. if (settings.getBoolean(Keys.git.enableGitServlet, true)) {
  145. AccessPermission permission = user.getRepositoryPermission(repository).permission;
  146. if (permission.exceeds(AccessPermission.NONE)) {
  147. Transport transport = Transport.fromString(request.getScheme());
  148. if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(transport)) {
  149. // downgrade the repo permission for this transport
  150. // because it is not an acceptable PUSH transport
  151. permission = AccessPermission.CLONE;
  152. }
  153. list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission));
  154. }
  155. }
  156. // ssh daemon url
  157. String sshDaemonUrl = servicesManager.getSshDaemonUrl(request, user, repository);
  158. if (!StringUtils.isEmpty(sshDaemonUrl)) {
  159. AccessPermission permission = user.getRepositoryPermission(repository).permission;
  160. if (permission.exceeds(AccessPermission.NONE)) {
  161. if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.SSH)) {
  162. // downgrade the repo permission for this transport
  163. // because it is not an acceptable PUSH transport
  164. permission = AccessPermission.CLONE;
  165. }
  166. list.add(new RepositoryUrl(sshDaemonUrl, permission));
  167. }
  168. }
  169. // git daemon url
  170. String gitDaemonUrl = servicesManager.getGitDaemonUrl(request, user, repository);
  171. if (!StringUtils.isEmpty(gitDaemonUrl)) {
  172. AccessPermission permission = servicesManager.getGitDaemonAccessPermission(user, repository);
  173. if (permission.exceeds(AccessPermission.NONE)) {
  174. if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.GIT)) {
  175. // downgrade the repo permission for this transport
  176. // because it is not an acceptable PUSH transport
  177. permission = AccessPermission.CLONE;
  178. }
  179. list.add(new RepositoryUrl(gitDaemonUrl, permission));
  180. }
  181. }
  182. // add all other urls
  183. // {0} = repository
  184. // {1} = username
  185. for (String url : settings.getStrings(Keys.web.otherUrls)) {
  186. if (url.contains("{1}")) {
  187. // external url requires username, only add url IF we have one
  188. if (!StringUtils.isEmpty(username)) {
  189. list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null));
  190. }
  191. } else {
  192. // external url does not require username
  193. list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null));
  194. }
  195. }
  196. // sort transports by highest permission and then by transport security
  197. Collections.sort(list, new Comparator<RepositoryUrl>() {
  198. @Override
  199. public int compare(RepositoryUrl o1, RepositoryUrl o2) {
  200. if (!o1.isExternal() && o2.isExternal()) {
  201. // prefer Gitblit over external
  202. return -1;
  203. } else if (o1.isExternal() && !o2.isExternal()) {
  204. // prefer Gitblit over external
  205. return 1;
  206. } else if (o1.isExternal() && o2.isExternal()) {
  207. // sort by Transport ordinal
  208. return o1.transport.compareTo(o2.transport);
  209. } else if (o1.permission.exceeds(o2.permission)) {
  210. // prefer highest permission
  211. return -1;
  212. } else if (o2.permission.exceeds(o1.permission)) {
  213. // prefer highest permission
  214. return 1;
  215. }
  216. // prefer more secure transports
  217. return o1.transport.compareTo(o2.transport);
  218. }
  219. });
  220. return list;
  221. }
  222. /**
  223. * Detect renames and reindex as appropriate.
  224. */
  225. @Override
  226. public void updateRepositoryModel(String repositoryName, RepositoryModel repository,
  227. boolean isCreate) throws GitBlitException {
  228. RepositoryModel oldModel = null;
  229. boolean isRename = !isCreate && !repositoryName.equalsIgnoreCase(repository.name);
  230. if (isRename) {
  231. oldModel = repositoryManager.getRepositoryModel(repositoryName);
  232. }
  233. super.updateRepositoryModel(repositoryName, repository, isCreate);
  234. if (isRename && ticketService != null) {
  235. ticketService.rename(oldModel, repository);
  236. }
  237. }
  238. /**
  239. * Delete the user and all associated public ssh keys.
  240. */
  241. @Override
  242. public boolean deleteUser(String username) {
  243. UserModel user = userManager.getUserModel(username);
  244. return deleteUserModel(user);
  245. }
  246. @Override
  247. public boolean deleteUserModel(UserModel model) {
  248. boolean success = userManager.deleteUserModel(model);
  249. if (success) {
  250. getPublicKeyManager().removeAllKeys(model.username);
  251. }
  252. return success;
  253. }
  254. /**
  255. * Delete the repository and all associated tickets.
  256. */
  257. @Override
  258. public boolean deleteRepository(String repositoryName) {
  259. RepositoryModel repository = repositoryManager.getRepositoryModel(repositoryName);
  260. return deleteRepositoryModel(repository);
  261. }
  262. @Override
  263. public boolean deleteRepositoryModel(RepositoryModel model) {
  264. boolean success = repositoryManager.deleteRepositoryModel(model);
  265. if (success && ticketService != null) {
  266. ticketService.deleteAll(model);
  267. }
  268. return success;
  269. }
  270. /**
  271. * Returns the configured ticket service.
  272. *
  273. * @return a ticket service
  274. */
  275. @Override
  276. public ITicketService getTicketService() {
  277. return ticketService;
  278. }
  279. protected void configureTicketService() {
  280. String clazz = settings.getString(Keys.tickets.service, NullTicketService.class.getName());
  281. if (StringUtils.isEmpty(clazz)) {
  282. clazz = NullTicketService.class.getName();
  283. }
  284. try {
  285. Class<? extends ITicketService> serviceClass = (Class<? extends ITicketService>) Class.forName(clazz);
  286. ticketService = injector.get(serviceClass).start();
  287. if (ticketService instanceof NullTicketService) {
  288. logger.warn("No ticket service configured.");
  289. } else if (ticketService.isReady()) {
  290. logger.info("{} is ready.", ticketService);
  291. } else {
  292. logger.warn("{} is disabled.", ticketService);
  293. }
  294. } catch (Exception e) {
  295. logger.error("failed to create ticket service " + clazz, e);
  296. ticketService = injector.get(NullTicketService.class).start();
  297. }
  298. }
  299. /**
  300. * A nested Dagger graph is used for constructor dependency injection of
  301. * complex classes.
  302. *
  303. * @author James Moger
  304. *
  305. */
  306. @Module(
  307. library = true,
  308. injects = {
  309. IStoredSettings.class,
  310. // core managers
  311. IRuntimeManager.class,
  312. INotificationManager.class,
  313. IUserManager.class,
  314. IAuthenticationManager.class,
  315. IRepositoryManager.class,
  316. IProjectManager.class,
  317. IFederationManager.class,
  318. // the monolithic manager
  319. IGitblit.class,
  320. // ticket services
  321. NullTicketService.class,
  322. FileTicketService.class,
  323. BranchTicketService.class,
  324. RedisTicketService.class
  325. }
  326. )
  327. class GitBlitModule {
  328. @Provides @Singleton IStoredSettings provideSettings() {
  329. return settings;
  330. }
  331. @Provides @Singleton IRuntimeManager provideRuntimeManager() {
  332. return runtimeManager;
  333. }
  334. @Provides @Singleton INotificationManager provideNotificationManager() {
  335. return notificationManager;
  336. }
  337. @Provides @Singleton IUserManager provideUserManager() {
  338. return userManager;
  339. }
  340. @Provides @Singleton IAuthenticationManager provideAuthenticationManager() {
  341. return authenticationManager;
  342. }
  343. @Provides @Singleton IRepositoryManager provideRepositoryManager() {
  344. return repositoryManager;
  345. }
  346. @Provides @Singleton IProjectManager provideProjectManager() {
  347. return projectManager;
  348. }
  349. @Provides @Singleton IFederationManager provideFederationManager() {
  350. return federationManager;
  351. }
  352. @Provides @Singleton IGitblit provideGitblit() {
  353. return GitBlit.this;
  354. }
  355. @Provides @Singleton NullTicketService provideNullTicketService() {
  356. return new NullTicketService(
  357. runtimeManager,
  358. notificationManager,
  359. userManager,
  360. repositoryManager);
  361. }
  362. @Provides @Singleton FileTicketService provideFileTicketService() {
  363. return new FileTicketService(
  364. runtimeManager,
  365. notificationManager,
  366. userManager,
  367. repositoryManager);
  368. }
  369. @Provides @Singleton BranchTicketService provideBranchTicketService() {
  370. return new BranchTicketService(
  371. runtimeManager,
  372. notificationManager,
  373. userManager,
  374. repositoryManager);
  375. }
  376. @Provides @Singleton RedisTicketService provideRedisTicketService() {
  377. return new RedisTicketService(
  378. runtimeManager,
  379. notificationManager,
  380. userManager,
  381. repositoryManager);
  382. }
  383. }
  384. }