PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/javamelody-core/src/main/java/net/bull/javamelody/JiraMonitoringFilter.java

https://gitlab.com/tuandung.bui/javamelody
Java | 366 lines | 267 code | 26 blank | 73 comment | 46 complexity | 8dc38d80f2013e57e975eeb2fe12e9f7 MD5 | raw file
  1. /*
  2. * Copyright 2008-2016 by Emeric Vernat
  3. *
  4. * This file is part of Java Melody.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package net.bull.javamelody;
  19. import java.io.IOException;
  20. import java.security.Principal;
  21. import java.util.Arrays;
  22. import java.util.List;
  23. import javax.servlet.FilterChain;
  24. import javax.servlet.FilterConfig;
  25. import javax.servlet.ServletException;
  26. import javax.servlet.ServletRequest;
  27. import javax.servlet.ServletResponse;
  28. import javax.servlet.http.HttpServletRequest;
  29. import javax.servlet.http.HttpServletResponse;
  30. import javax.servlet.http.HttpSession;
  31. /**
  32. * Filter of monitoring JavaMelody for JIRA/Bamboo/Confluence with security check for system administrator.
  33. * @author Emeric Vernat
  34. */
  35. public class JiraMonitoringFilter extends PluginMonitoringFilter {
  36. private static final boolean PLUGIN_AUTHENTICATION_DISABLED = Boolean
  37. .parseBoolean(System.getProperty("javamelody.plugin-authentication-disabled"));
  38. // valeur de com.atlassian.jira.security.Permissions.SYSTEM_ADMIN
  39. private static final int SYSTEM_ADMIN = 44;
  40. // valeur de DefaultAuthenticator.LOGGED_IN_KEY
  41. private static final String LOGGED_IN_KEY = "seraph_defaultauthenticator_user";
  42. private static final List<String> JIRA_USER_CLASSES = Arrays.asList(
  43. // since JIRA 6, but exists in JIRA 5.2:
  44. "com.atlassian.jira.user.ApplicationUser",
  45. // since JIRA 5:
  46. "com.atlassian.crowd.embedded.api.User",
  47. // before JIRA 5:
  48. "com.opensymphony.user.User");
  49. // initialisation ici et non dans la méthode init, car on ne sait pas très bien
  50. // quand la méthode init serait appelée dans les systèmes de plugins
  51. private final boolean jira = isJira();
  52. private final boolean confluence = isConfluence();
  53. private final boolean bamboo = isBamboo();
  54. private boolean confluenceGetUserByNameExists = true; // on suppose true au départ
  55. /** {@inheritDoc} */
  56. @Override
  57. public void init(FilterConfig config) throws ServletException {
  58. super.init(config);
  59. if (jira) {
  60. LOG.debug("JavaMelody is monitoring JIRA");
  61. } else if (confluence) {
  62. LOG.debug("JavaMelody is monitoring Confluence");
  63. } else if (bamboo) {
  64. LOG.debug("JavaMelody is monitoring Bamboo");
  65. } else {
  66. LOG.debug(
  67. "JavaMelody is monitoring unknown, access to monitoring reports is not secured by JavaMelody");
  68. }
  69. if (PLUGIN_AUTHENTICATION_DISABLED) {
  70. LOG.debug("Authentication for monitoring reports has been disabled");
  71. }
  72. final String analyticsDisabled = "javamelody.analytics-disabled";
  73. if (System.getProperty(analyticsDisabled) != null
  74. || config.getServletContext().getInitParameter(analyticsDisabled) != null) {
  75. System.setProperty("javamelody.analytics-id", "disabled");
  76. }
  77. }
  78. /** {@inheritDoc} */
  79. @Override
  80. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  81. throws IOException, ServletException {
  82. if (!(request instanceof HttpServletRequest)) {
  83. super.doFilter(request, response, chain);
  84. return;
  85. }
  86. final HttpServletRequest httpRequest = (HttpServletRequest) request;
  87. final HttpServletResponse httpResponse = (HttpServletResponse) response;
  88. if (httpRequest.getRequestURI().equals(getMonitoringUrl(httpRequest))
  89. && hasNotPermission(httpRequest, httpResponse)) {
  90. return;
  91. }
  92. putRemoteUserInSession(httpRequest);
  93. super.doFilter(request, response, chain);
  94. }
  95. private void putRemoteUserInSession(HttpServletRequest httpRequest) {
  96. final HttpSession session = httpRequest.getSession(false);
  97. if (session != null
  98. && session.getAttribute(SessionInformations.SESSION_REMOTE_USER) == null) {
  99. // si session null, la session n'est pas encore créée (et ne le sera peut-être jamais),
  100. try {
  101. final Object user = getUser(session);
  102. // objet utilisateur, peut être null
  103. if (user instanceof Principal) {
  104. final String remoteUser = ((Principal) user).getName();
  105. session.setAttribute(SessionInformations.SESSION_REMOTE_USER, remoteUser);
  106. }
  107. } catch (final Exception e) {
  108. // tant pis
  109. return;
  110. }
  111. }
  112. }
  113. private boolean hasNotPermission(HttpServletRequest httpRequest,
  114. HttpServletResponse httpResponse) throws IOException {
  115. return !PLUGIN_AUTHENTICATION_DISABLED
  116. && (jira && !checkJiraAdminPermission(httpRequest, httpResponse)
  117. || confluence && !checkConfluenceAdminPermission(httpRequest, httpResponse)
  118. || bamboo && !checkBambooAdminPermission(httpRequest, httpResponse));
  119. }
  120. private boolean checkJiraAdminPermission(HttpServletRequest httpRequest,
  121. HttpServletResponse httpResponse) throws IOException {
  122. // only the administrator can view the monitoring report
  123. final Object user = getUser(httpRequest);
  124. if (user == null) {
  125. // si non authentifié, on redirige vers la page de login en indiquant la page
  126. // d'origine (sans le contexte) à afficher après le login
  127. final String destination = getMonitoringUrl(httpRequest)
  128. .substring(httpRequest.getContextPath().length());
  129. httpResponse.sendRedirect("login.jsp?os_destination=" + destination);
  130. return false;
  131. }
  132. if (!hasJiraSystemAdminPermission(user)) {
  133. // si authentifié mais sans la permission system admin, alors Forbidden
  134. httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
  135. return false;
  136. }
  137. return true;
  138. }
  139. private boolean checkConfluenceAdminPermission(HttpServletRequest httpRequest,
  140. HttpServletResponse httpResponse) throws IOException {
  141. // only the administrator can view the monitoring report
  142. final Object user = getUser(httpRequest);
  143. if (user == null) {
  144. // si non authentifié, on redirige vers la page de login en indiquant la page
  145. // d'origine (sans le contexte) à afficher après le login
  146. final String destination = getMonitoringUrl(httpRequest)
  147. .substring(httpRequest.getContextPath().length());
  148. httpResponse.sendRedirect("login.action?os_destination=" + destination);
  149. return false;
  150. }
  151. if (!hasConfluenceAdminPermission(user)) {
  152. // si authentifié mais sans la permission system admin, alors Forbidden
  153. httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
  154. return false;
  155. }
  156. return true;
  157. }
  158. private boolean checkBambooAdminPermission(HttpServletRequest httpRequest,
  159. HttpServletResponse httpResponse) throws IOException {
  160. // only the administrator can view the monitoring report
  161. final Object user = getUser(httpRequest);
  162. if (user == null) {
  163. // si non authentifié, on redirige vers la page de login en indiquant la page
  164. // d'origine (sans le contexte) à afficher après le login
  165. final String destination = getMonitoringUrl(httpRequest)
  166. .substring(httpRequest.getContextPath().length());
  167. httpResponse.sendRedirect("userlogin!default.action?os_destination=" + destination);
  168. return false;
  169. }
  170. if (!hasBambooAdminPermission(user)) {
  171. // si authentifié mais sans la permission admin, alors Forbidden
  172. httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access");
  173. return false;
  174. }
  175. return true;
  176. }
  177. private static boolean hasJiraSystemAdminPermission(Object user) {
  178. try {
  179. final Class<?> componentAccessorClass = Class
  180. .forName("com.atlassian.jira.component.ComponentAccessor");
  181. // on travaille par réflexion car la compilation normale introduirait une dépendance
  182. // trop compliquée et trop lourde à télécharger pour maven
  183. // Note : si getPermissionManager().hasPermission est supprimée,
  184. // il faudra utiliser getGlobalPermissionManager().hasPermission (Since v6.2.5)
  185. final Object permissionManager = componentAccessorClass
  186. .getMethod("getPermissionManager").invoke(null);
  187. Exception firstException = null;
  188. // selon la version de JIRA, on essaye les différentes classes possibles du user
  189. for (final String className : JIRA_USER_CLASSES) {
  190. try {
  191. final Class<?> userClass = Class.forName(className);
  192. final Boolean result = (Boolean) permissionManager.getClass()
  193. .getMethod("hasPermission", Integer.TYPE, userClass)
  194. .invoke(permissionManager, SYSTEM_ADMIN, user);
  195. return result;
  196. } catch (final Exception e) {
  197. if (firstException == null) {
  198. firstException = e;
  199. }
  200. continue;
  201. }
  202. }
  203. // aucune classe n'a fonctionné
  204. throw firstException;
  205. } catch (final Exception e) {
  206. throw new IllegalStateException(e);
  207. }
  208. // return user != null
  209. // && com.atlassian.jira.component.ComponentAccessor.getPermissionManager().hasPermission(
  210. // SYSTEM_ADMIN, (com.opensymphony.user.User) user);
  211. }
  212. private static boolean hasConfluenceAdminPermission(Object user) {
  213. try {
  214. final Class<?> containerManagerClass = Class
  215. .forName("com.atlassian.spring.container.ContainerManager");
  216. final Class<?> userClass = Class.forName("com.atlassian.user.User");
  217. // on travaille par réflexion car la compilation normale introduirait une dépendance
  218. // trop compliquée et trop lourde à télécharger pour maven
  219. final Object permissionManager = containerManagerClass
  220. .getMethod("getComponent", String.class).invoke(null, "permissionManager");
  221. final Boolean result = (Boolean) permissionManager.getClass()
  222. .getMethod("isConfluenceAdministrator", userClass)
  223. .invoke(permissionManager, user);
  224. return result;
  225. } catch (final Exception e) {
  226. throw new IllegalStateException(e);
  227. }
  228. // return user != null
  229. // && com.atlassian.spring.container.ContainerManager.getComponent("permissionManager").
  230. // isConfluenceAdministrator((com.opensymphony.user.User) user);
  231. }
  232. private static boolean hasBambooAdminPermission(Object user) {
  233. try {
  234. final Class<?> containerManagerClass = Class
  235. .forName("com.atlassian.spring.container.ContainerManager");
  236. // on travaille par réflexion car la compilation normale introduirait une dépendance
  237. // trop compliquée et trop lourde à télécharger pour maven
  238. final Object bambooPermissionManager = containerManagerClass
  239. .getMethod("getComponent", String.class)
  240. .invoke(null, "bambooPermissionManager");
  241. Boolean result;
  242. try {
  243. // since Bamboo 3.1 (issue 192):
  244. result = (Boolean) bambooPermissionManager.getClass()
  245. .getMethod("isSystemAdmin", String.class)
  246. .invoke(bambooPermissionManager, user.toString());
  247. } catch (final NoSuchMethodException e) {
  248. // before Bamboo 3.1 (issue 192):
  249. final Class<?> globalApplicationSecureObjectClass = Class
  250. .forName("com.atlassian.bamboo.security.GlobalApplicationSecureObject");
  251. final Object globalApplicationSecureObject = globalApplicationSecureObjectClass
  252. .getField("INSTANCE").get(null);
  253. result = (Boolean) bambooPermissionManager.getClass()
  254. .getMethod("hasPermission", String.class, String.class, Object.class)
  255. .invoke(bambooPermissionManager, user.toString(), "ADMIN",
  256. globalApplicationSecureObject);
  257. }
  258. return result;
  259. } catch (final Exception e) {
  260. throw new IllegalStateException(e);
  261. }
  262. // return user != null
  263. // && com.atlassian.spring.container.ContainerManager.getComponent("bambooPermissionManager").
  264. // hasPermission(username, "ADMIN", GlobalApplicationSecureObject.INSTANCE);
  265. }
  266. private Object getUser(HttpServletRequest httpRequest) {
  267. final HttpSession session = httpRequest.getSession(false);
  268. return getUser(session);
  269. }
  270. private Object getUser(HttpSession session) {
  271. // ceci fonctionne dans JIRA et dans Confluence (et Bamboo ?)
  272. if (session == null) {
  273. return null;
  274. }
  275. Object result = session.getAttribute(LOGGED_IN_KEY);
  276. if (confluence) {
  277. if (result != null && "com.atlassian.confluence.user.SessionSafePrincipal"
  278. .equals(result.getClass().getName())) {
  279. // since confluence 4.1.4 (or 4.1.?)
  280. final String userName = result.toString();
  281. // note: httpRequest.getRemoteUser() null in general
  282. try {
  283. final Class<?> containerManagerClass = Class
  284. .forName("com.atlassian.spring.container.ContainerManager");
  285. final Object userAccessor = containerManagerClass
  286. .getMethod("getComponent", String.class).invoke(null, "userAccessor");
  287. result = userAccessor.getClass().getMethod("getUser", String.class)
  288. .invoke(userAccessor, userName);
  289. } catch (final Exception e) {
  290. throw new IllegalStateException(e);
  291. }
  292. } else if (result instanceof Principal && confluenceGetUserByNameExists) {
  293. // since confluence 5.2 or 5.3
  294. final String userName = ((Principal) result).getName();
  295. try {
  296. final Class<?> containerManagerClass = Class
  297. .forName("com.atlassian.spring.container.ContainerManager");
  298. final Object userAccessor = containerManagerClass
  299. .getMethod("getComponent", String.class).invoke(null, "userAccessor");
  300. // getUser deprecated, use getUserByName as said in:
  301. // https://docs.atlassian.com/atlassian-confluence/5.3.1/com/atlassian/confluence/user/UserAccessor.html
  302. try {
  303. result = userAccessor.getClass().getMethod("getUserByName", String.class)
  304. .invoke(userAccessor, userName);
  305. } catch (final NoSuchMethodException e) {
  306. // getUserByName does not exist in old Confluence versions (3.5.13 for example)
  307. confluenceGetUserByNameExists = false;
  308. }
  309. } catch (final Exception e) {
  310. throw new IllegalStateException(e);
  311. }
  312. }
  313. }
  314. return result;
  315. }
  316. private static boolean isJira() {
  317. try {
  318. Class.forName("com.atlassian.jira.ManagerFactory");
  319. return true;
  320. } catch (final ClassNotFoundException e) {
  321. return false;
  322. }
  323. }
  324. private static boolean isConfluence() {
  325. try {
  326. Class.forName("com.atlassian.confluence.security.PermissionManager");
  327. return true;
  328. } catch (final ClassNotFoundException e) {
  329. return false;
  330. }
  331. }
  332. private static boolean isBamboo() {
  333. try {
  334. Class.forName("com.atlassian.bamboo.security.BambooPermissionManager");
  335. return true;
  336. } catch (final ClassNotFoundException e) {
  337. return false;
  338. }
  339. }
  340. }