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.

RootPage.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  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.wicket.pages;
  17. import java.text.MessageFormat;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Calendar;
  21. import java.util.Collections;
  22. import java.util.Date;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.LinkedHashSet;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import java.util.concurrent.atomic.AtomicInteger;
  30. import java.util.regex.Pattern;
  31. import org.apache.wicket.PageParameters;
  32. import org.apache.wicket.markup.html.form.PasswordTextField;
  33. import org.apache.wicket.markup.html.form.TextField;
  34. import org.apache.wicket.model.IModel;
  35. import org.apache.wicket.model.Model;
  36. import org.apache.wicket.protocol.http.WebResponse;
  37. import com.gitblit.Constants;
  38. import com.gitblit.GitBlit;
  39. import com.gitblit.Keys;
  40. import com.gitblit.models.RepositoryModel;
  41. import com.gitblit.models.TeamModel;
  42. import com.gitblit.models.UserModel;
  43. import com.gitblit.utils.StringUtils;
  44. import com.gitblit.wicket.GitBlitWebSession;
  45. import com.gitblit.wicket.PageRegistration;
  46. import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
  47. import com.gitblit.wicket.SessionlessForm;
  48. import com.gitblit.wicket.WicketUtils;
  49. import com.gitblit.wicket.panels.NavigationPanel;
  50. /**
  51. * Root page is a topbar, navigable page like Repositories, Users, or
  52. * Federation.
  53. *
  54. * @author James Moger
  55. *
  56. */
  57. public abstract class RootPage extends BasePage {
  58. boolean showAdmin;
  59. IModel<String> username = new Model<String>("");
  60. IModel<String> password = new Model<String>("");
  61. List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
  62. public RootPage() {
  63. super();
  64. }
  65. public RootPage(PageParameters params) {
  66. super(params);
  67. }
  68. @Override
  69. protected void setupPage(String repositoryName, String pageName) {
  70. boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false);
  71. boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
  72. boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
  73. if (authenticateAdmin) {
  74. showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
  75. // authentication requires state and session
  76. setStatelessHint(false);
  77. } else {
  78. showAdmin = allowAdmin;
  79. if (authenticateView) {
  80. // authentication requires state and session
  81. setStatelessHint(false);
  82. } else {
  83. // no authentication required, no state and no session required
  84. setStatelessHint(true);
  85. }
  86. }
  87. boolean showRegistrations = GitBlit.canFederate()
  88. && GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
  89. // navigation links
  90. List<PageRegistration> pages = new ArrayList<PageRegistration>();
  91. pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
  92. getRootPageParameters()));
  93. pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
  94. if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)) {
  95. pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
  96. }
  97. if (showAdmin) {
  98. pages.add(new PageRegistration("gb.users", UsersPage.class));
  99. }
  100. if (showAdmin || showRegistrations) {
  101. pages.add(new PageRegistration("gb.federation", FederationPage.class));
  102. }
  103. if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
  104. addDropDownMenus(pages);
  105. }
  106. NavigationPanel navPanel = new NavigationPanel("navPanel", getClass(), pages);
  107. add(navPanel);
  108. // login form
  109. SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", getClass(), getPageParameters()) {
  110. private static final long serialVersionUID = 1L;
  111. @Override
  112. public void onSubmit() {
  113. String username = RootPage.this.username.getObject();
  114. char[] password = RootPage.this.password.getObject().toCharArray();
  115. UserModel user = GitBlit.self().authenticate(username, password);
  116. if (user == null) {
  117. error(getString("gb.invalidUsernameOrPassword"));
  118. } else if (user.username.equals(Constants.FEDERATION_USER)) {
  119. // disallow the federation user from logging in via the
  120. // web ui
  121. error(getString("gb.invalidUsernameOrPassword"));
  122. user = null;
  123. } else {
  124. loginUser(user);
  125. }
  126. }
  127. };
  128. TextField<String> unameField = new TextField<String>("username", username);
  129. WicketUtils.setInputPlaceholder(unameField, getString("gb.username"));
  130. loginForm.add(unameField);
  131. PasswordTextField pwField = new PasswordTextField("password", password);
  132. WicketUtils.setInputPlaceholder(pwField, getString("gb.password"));
  133. loginForm.add(pwField);
  134. add(loginForm);
  135. if (authenticateView || authenticateAdmin) {
  136. loginForm.setVisible(!GitBlitWebSession.get().isLoggedIn());
  137. } else {
  138. loginForm.setVisible(false);
  139. }
  140. // display an error message cached from a redirect
  141. String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
  142. if (!StringUtils.isEmpty(cachedMessage)) {
  143. error(cachedMessage);
  144. } else if (showAdmin) {
  145. int pendingProposals = GitBlit.self().getPendingFederationProposals().size();
  146. if (pendingProposals == 1) {
  147. info(getString("gb.OneProposalToReview"));
  148. } else if (pendingProposals > 1) {
  149. info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
  150. pendingProposals));
  151. }
  152. }
  153. super.setupPage(repositoryName, pageName);
  154. }
  155. private PageParameters getRootPageParameters() {
  156. if (reusePageParameters()) {
  157. PageParameters pp = getPageParameters();
  158. if (pp != null) {
  159. PageParameters params = new PageParameters(pp);
  160. // remove named project parameter
  161. params.remove("p");
  162. // remove named repository parameter
  163. params.remove("r");
  164. // remove named user parameter
  165. params.remove("user");
  166. // remove days back parameter if it is the default value
  167. if (params.containsKey("db")
  168. && params.getInt("db") == GitBlit.getInteger(Keys.web.activityDuration, 14)) {
  169. params.remove("db");
  170. }
  171. return params;
  172. }
  173. }
  174. return null;
  175. }
  176. protected boolean reusePageParameters() {
  177. return false;
  178. }
  179. private void loginUser(UserModel user) {
  180. if (user != null) {
  181. // Set the user into the session
  182. GitBlitWebSession session = GitBlitWebSession.get();
  183. // issue 62: fix session fixation vulnerability
  184. session.replaceSession();
  185. session.setUser(user);
  186. // Set Cookie
  187. if (GitBlit.getBoolean(Keys.web.allowCookieAuthentication, false)) {
  188. WebResponse response = (WebResponse) getRequestCycle().getResponse();
  189. GitBlit.self().setCookie(response, user);
  190. }
  191. if (!session.continueRequest()) {
  192. PageParameters params = getPageParameters();
  193. if (params == null) {
  194. // redirect to this page
  195. setResponsePage(getClass());
  196. } else {
  197. // Strip username and password and redirect to this page
  198. params.remove("username");
  199. params.remove("password");
  200. setResponsePage(getClass(), params);
  201. }
  202. }
  203. }
  204. }
  205. protected List<RepositoryModel> getRepositoryModels() {
  206. if (repositoryModels.isEmpty()) {
  207. final UserModel user = GitBlitWebSession.get().getUser();
  208. List<RepositoryModel> repositories = GitBlit.self().getRepositoryModels(user);
  209. repositoryModels.addAll(repositories);
  210. Collections.sort(repositoryModels);
  211. }
  212. return repositoryModels;
  213. }
  214. protected void addDropDownMenus(List<PageRegistration> pages) {
  215. }
  216. protected List<DropDownMenuItem> getRepositoryFilterItems(PageParameters params) {
  217. final UserModel user = GitBlitWebSession.get().getUser();
  218. Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>();
  219. List<RepositoryModel> repositories = getRepositoryModels();
  220. // accessible repositories by federation set
  221. Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
  222. for (RepositoryModel repository : repositories) {
  223. for (String set : repository.federationSets) {
  224. String key = set.toLowerCase();
  225. if (setMap.containsKey(key)) {
  226. setMap.get(key).incrementAndGet();
  227. } else {
  228. setMap.put(key, new AtomicInteger(1));
  229. }
  230. }
  231. }
  232. if (setMap.size() > 0) {
  233. List<String> sets = new ArrayList<String>(setMap.keySet());
  234. Collections.sort(sets);
  235. for (String set : sets) {
  236. filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", set,
  237. setMap.get(set).get()), "set", set, params));
  238. }
  239. // divider
  240. filters.add(new DropDownMenuItem());
  241. }
  242. // user's team memberships
  243. if (user != null && user.teams.size() > 0) {
  244. List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
  245. Collections.sort(teams);
  246. for (TeamModel team : teams) {
  247. filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", team.name,
  248. team.repositories.size()), "team", team.name, params));
  249. }
  250. // divider
  251. filters.add(new DropDownMenuItem());
  252. }
  253. // custom filters
  254. String customFilters = GitBlit.getString(Keys.web.customFilters, null);
  255. if (!StringUtils.isEmpty(customFilters)) {
  256. boolean addedExpression = false;
  257. List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
  258. for (String expression : expressions) {
  259. if (!StringUtils.isEmpty(expression)) {
  260. addedExpression = true;
  261. filters.add(new DropDownMenuItem(null, "x", expression, params));
  262. }
  263. }
  264. // if we added any custom expressions, add a divider
  265. if (addedExpression) {
  266. filters.add(new DropDownMenuItem());
  267. }
  268. }
  269. return new ArrayList<DropDownMenuItem>(filters);
  270. }
  271. protected List<DropDownMenuItem> getTimeFilterItems(PageParameters params) {
  272. // days back choices - additive parameters
  273. int daysBack = GitBlit.getInteger(Keys.web.activityDuration, 14);
  274. if (daysBack < 1) {
  275. daysBack = 14;
  276. }
  277. List<DropDownMenuItem> items = new ArrayList<DropDownMenuItem>();
  278. Set<Integer> choicesSet = new HashSet<Integer>(Arrays.asList(daysBack, 14, 28, 60, 90, 180));
  279. List<Integer> choices = new ArrayList<Integer>(choicesSet);
  280. Collections.sort(choices);
  281. String lastDaysPattern = getString("gb.lastNDays");
  282. for (Integer db : choices) {
  283. String txt = MessageFormat.format(lastDaysPattern, db);
  284. items.add(new DropDownMenuItem(txt, "db", db.toString(), params));
  285. }
  286. items.add(new DropDownMenuItem());
  287. return items;
  288. }
  289. protected List<RepositoryModel> getRepositories(PageParameters params) {
  290. if (params == null) {
  291. return getRepositoryModels();
  292. }
  293. boolean hasParameter = false;
  294. String projectName = WicketUtils.getProjectName(params);
  295. String userName = WicketUtils.getUsername(params);
  296. if (StringUtils.isEmpty(projectName)) {
  297. if (!StringUtils.isEmpty(userName)) {
  298. projectName = "~" + userName;
  299. }
  300. }
  301. String repositoryName = WicketUtils.getRepositoryName(params);
  302. String set = WicketUtils.getSet(params);
  303. String regex = WicketUtils.getRegEx(params);
  304. String team = WicketUtils.getTeam(params);
  305. int daysBack = params.getInt("db", 0);
  306. List<RepositoryModel> availableModels = getRepositoryModels();
  307. Set<RepositoryModel> models = new HashSet<RepositoryModel>();
  308. if (!StringUtils.isEmpty(repositoryName)) {
  309. // try named repository
  310. hasParameter = true;
  311. for (RepositoryModel model : availableModels) {
  312. if (model.name.equalsIgnoreCase(repositoryName)) {
  313. models.add(model);
  314. break;
  315. }
  316. }
  317. }
  318. if (!StringUtils.isEmpty(projectName)) {
  319. // try named project
  320. hasParameter = true;
  321. if (projectName.equalsIgnoreCase(GitBlit.getString(Keys.web.repositoryRootGroupName, "main"))) {
  322. // root project/group
  323. for (RepositoryModel model : availableModels) {
  324. if (model.name.indexOf('/') == -1) {
  325. models.add(model);
  326. }
  327. }
  328. } else {
  329. // named project/group
  330. String group = projectName.toLowerCase() + "/";
  331. for (RepositoryModel model : availableModels) {
  332. if (model.name.toLowerCase().startsWith(group)) {
  333. models.add(model);
  334. }
  335. }
  336. }
  337. }
  338. if (!StringUtils.isEmpty(regex)) {
  339. // filter the repositories by the regex
  340. hasParameter = true;
  341. Pattern pattern = Pattern.compile(regex);
  342. for (RepositoryModel model : availableModels) {
  343. if (pattern.matcher(model.name).find()) {
  344. models.add(model);
  345. }
  346. }
  347. }
  348. if (!StringUtils.isEmpty(set)) {
  349. // filter the repositories by the specified sets
  350. hasParameter = true;
  351. List<String> sets = StringUtils.getStringsFromValue(set, ",");
  352. for (RepositoryModel model : availableModels) {
  353. for (String curr : sets) {
  354. if (model.federationSets.contains(curr)) {
  355. models.add(model);
  356. }
  357. }
  358. }
  359. }
  360. if (!StringUtils.isEmpty(team)) {
  361. // filter the repositories by the specified teams
  362. hasParameter = true;
  363. List<String> teams = StringUtils.getStringsFromValue(team, ",");
  364. // need TeamModels first
  365. List<TeamModel> teamModels = new ArrayList<TeamModel>();
  366. for (String name : teams) {
  367. TeamModel teamModel = GitBlit.self().getTeamModel(name);
  368. if (teamModel != null) {
  369. teamModels.add(teamModel);
  370. }
  371. }
  372. // brute-force our way through finding the matching models
  373. for (RepositoryModel repositoryModel : availableModels) {
  374. for (TeamModel teamModel : teamModels) {
  375. if (teamModel.hasRepository(repositoryModel.name)) {
  376. models.add(repositoryModel);
  377. }
  378. }
  379. }
  380. }
  381. if (!hasParameter) {
  382. models.addAll(availableModels);
  383. }
  384. // time-filter the list
  385. if (daysBack > 0) {
  386. Calendar cal = Calendar.getInstance();
  387. cal.set(Calendar.HOUR_OF_DAY, 0);
  388. cal.set(Calendar.MINUTE, 0);
  389. cal.set(Calendar.SECOND, 0);
  390. cal.set(Calendar.MILLISECOND, 0);
  391. cal.add(Calendar.DATE, -1 * daysBack);
  392. Date threshold = cal.getTime();
  393. Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
  394. for (RepositoryModel model : models) {
  395. if (model.lastChange.after(threshold)) {
  396. timeFiltered.add(model);
  397. }
  398. }
  399. models = timeFiltered;
  400. }
  401. List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
  402. Collections.sort(list);
  403. return list;
  404. }
  405. }